Python微型異步爬蟲框架

Amipy

Python微型異步爬蟲框架(A micro asynchronous Python website crawler framework)python

基於Python 3.5 + 的異步async-await 框架,搭建一個模塊化的微型異步爬蟲。能夠根據需求控制異步隊列的長度和延遲時間等。配置了能夠去重的布隆過濾器,網頁內容正文過濾等,徹底自主配置使用。

GitHub地址:源碼git

適用環境

  • windows 7 +
  • Python 3.5 +

安裝

直接使用pip安裝便可:github

pip install amipy

基礎命令

  • 1.查看當前路徑下的可用命令,在DOS命令行下輸入:
>amipy

會出現命令幫助界面。web

  • 2.建立一個新的項目,在DOS命令行下輸入:
>amipy cproject myproject

會在當前路徑下建立一個Amipy爬蟲項目myproject。若是想要建立在指定目錄下,能夠加上附加參數,-d,如:數據庫

> amipy cproject myproject -d  D:\somefolder

項目myproject便會在路徑D:somefolder下建立。
項目的目錄結構應該以下:json

--myproject
    |-spiders
    |   |-__init__.py
    |-__init__.py
    |-settings.py

其中:windows

settings.py 爲整個項目的配置文件,能夠爲整個項目下的爬蟲安裝共有的中間件,控制整個項目的請求併發數,設置日誌級別、文件路徑等。
  • 3.進入項目路徑,建立一個新的爬蟲,在DOS命令行下輸入:
>amipy cspider myspider

此時在項目myproject目錄下的spiders文件夾中會建立一個爬蟲目錄myspider,此時的項目結構爲:api

--myproject
    |-spiders
    |   |-__init__.py
    |   |-myspider
    |   |    |-__init__.py
    |   |    |-cookies.info
    |   |    |-item.py
    |   |    |-settings.py
    |   |    |-site_record.info
    |   |    |-spider.py
    |   |    |-url_record.info
    |-__init__.py
    |-settings.py
    |-log.log

其中:安全

  • 位於myspider文件夾下的settings.py爲爬蟲myspider的配置文件,該配置只對當前爬蟲有效。能夠對該爬蟲的布隆過濾器進行配置,安裝中間件等。
  • cookies.info 爲爬蟲的請求cookie保存文件,該爬蟲爬過的全部網站的cookie會保存進該文件。能夠經過爬蟲配置文件settings.py進行路徑加載和保存。
  • site_record.info 爲爬蟲爬取過的網站的布隆過濾器記錄文件,方便下次爬取的時候加載,會把爬取過的網站自動去掉。防止重複爬取。
  • url_record.info 爲該爬蟲發出的請求url+headers+method+數據的去重後集合,爬蟲結束運行時,若是配置保存去重url集合。下次爬取時加載該文件能夠自動過濾爬取過的全部url+headers+method+數據。
  • item.py 爲ORM的MongoDB數據集合對象,對應的類屬性能夠映射到數據庫集合中的字段,類名爲數據表名。
  • spider.py 爲當前爬蟲的主要文件,本身編寫爬取邏輯,提取規則和數據保存腳本等。
  • 4.運行項目下的全部爬蟲,進入項目路徑,在DOS命令行下輸入:
>amipy runproject

則該項目下的全部爬蟲會開始運行,若是不想運行某個爬蟲,只須要加上參數 -e,如:cookie

>amipy runproject -e No1spider No2spider

則名爲「No1spider」、「No2spider」的爬蟲均不會運行。

  • 5.運行指定的爬蟲,進入項目路徑,在DOS命令行下輸入:
>amipy runspider myspider01

則名爲「myspider01」的爬蟲便會被啓動。能夠加上多個爬蟲名稱,用空格隔開便可。

  • 6.列出當前項目下的全部爬蟲信息。在DOS命令行下輸入:
>amipy list

便會將當前項目下的全部爬蟲信息列出。

使用

Amipy爬蟲編寫流程

編寫本身的爬蟲。【假設你已經安裝前面"基礎命令"建立了一個項目,而且建立了一個爬蟲名爲myspider】只須要進入myspider文件夾,按照需求修改當前爬蟲的配置settings.py 以及數據存儲須要用到的表模型item.py編寫,編輯文件spider.py,加入爬取規則邏輯等。

Url類對象

Url類對象是一個規則匹配類,它提供了許多種模式的url規則匹配。
好比:

from amipy import Url
# 表示匹配到正則模式'http://www.170mv.com/song.*'的全部連接
Url(re='http://www.170mv.com/song.*')
# 表示匹配到正則模式'http://www.170mv.com/song.*'的全部連接其回調函數爲'getmp3'
Url(re='http://www.170mv.com/song/.*',callback='getmp3')
# 表示匹配到地址爲http協議,且路徑爲‘/novel/chapter1’,參數number=2的全部連接
Url(scheme='http',path='/novel/chapter1',params='number=2')
# 表示匹配到域名爲www.baidu.com的全部連接,爲該連接請求設置代理爲'127.0.0.1:1080'
Url(domain='www.baidu.com',proxy='127.0.0.1:1080')
# 表示匹配到域名爲www.baidu.com的全部連接,直接扔掉這些連接。
Url(domain='www.baidu.com',drop=True)

Url類應用的還在於黑白名單屬性中,如在爬蟲類中的屬性:

whitelist = [
        Url(re='http://www.170mv.com/song.*'),
        Url(re='http.*.sycdn.kuwo.cn.*'),]
blacklist = [
        Url(re='http://www.170mv.com/song.*'),
        Url(re='http.*.sycdn.kuwo.cn.*'),]

表示爬蟲請求的url黑白名單匹配規則。

必要屬性

打開spider.py ,能夠看到有兩個默認的必要屬性:

  • name 爬蟲的惟一標識,項目下不能有該屬性重名的爬蟲。
  • urls 起始連接種子,爬蟲開始的url列表

這兩個屬性是必須的。

回調函數

整個項目的主要實如今於回調函數的使用,利用異步請求獲得響應後立刻調用其請求綁定的回調函數來實現爬蟲的異步爬取。
請求後響應的回調函數(類方法)有:

  • parse 返回狀態200,請求正常響應正常,能夠編寫正常的規則提取、數據保存等。
  • error 狀態碼非200,出現異常狀態碼,編寫錯誤處理邏輯等。
  • exception 請求出現異常,異常自定義處理。

數據存儲

Amipy目前只支持MongoDB數據庫,默認的數據庫設置在爬蟲配置文件settings.py中。
對於爬取的數據進行保存,默認只使用MongoDB進行數據存儲(後續能夠本身擴展編寫ORM)。只須要打開item.py,修改其中的示例類,原先爲:

from amipy.BaseClass.orm import Model,Field
class DataItemName(Model):
    ...

修改其內容爲:

from amipy.BaseClass.orm import Model,Field
class MyTableName(Model):
    ID = Field('索引')
    content = Field('內容')

則類名 MyTableName爲保存在指定數據庫中的數據集合名稱,ID爲列對象,名稱爲「索引」,以此類推,content也爲列對象,名稱爲「內容」。
能夠按照本身的需求進行添加刪減列。

數據的保存只須要在回調函數中對對應的列對象進行賦值,然後調用ORM對象的save函數便可。好比在spider.py的爬蟲類中的成功回調函數parse中保存爬取到的數據:

...
    def parse(self,response):
        self.item.ID = 200
        self.item.content = '這是內容'
        self.item.save()
        ...

則 數據集合 MyTableName中會自動保存一行數據:列「索引」爲200,列「內容」爲「這是內容」的數據行。引用orm數據模型對象只須要調用爬蟲類的item屬性,如上面示例中的self.item便是。
獲取其數據庫對象可使用:self.item.db來得到當前爬蟲鏈接的MongoDB數據庫對象。
能夠經過

self.item.db.save()
self.item.db.delete()
self.item.db.update()
...

等api來實現數據庫操做。

事件循環loop

Amipy爬蟲的異步請求基於python3的協程async框架,因此項目全程只有一個事件循環運行,若是須要添加更多的爬蟲請求,能夠經過回調函數傳進事件循環,加入請求隊列。
具體作法即是經過在爬蟲類的回調函數中使用send函數來傳遞請求Request對象:

import amipy
from amipy import Request,send

class MySpider(amipy.Spider):
    ...
    
    def parse(self,response):
        ...
        # 加入新的爬蟲請求
        url = 'http://www.170mv.com/download/'
        send(Request(self,url))
        ...

能夠在項目配置文件settings.py中設置整個項目最大的協程併發數CONCURRENCY,以及協程請求的延時等。

Telnet鏈接

Amipy爬蟲內置一個服務線程,能夠經過Telnet進行鏈接來查看操做當前項目的爬蟲,在啓動爬蟲後,能夠經過新開一個DOS命令窗口,
輸入:

>telnet 127.0.0.1 2232

進行Telnet鏈接至項目服務線程,可使用的命令有:

show spiders         show all running spiders and their conditions.
   list                 list a general situation of all spiders.
   echo                 echo a running spider and its attributes.
   pause                pause a running spider by a give name.
   stop                 stop a running/paused spider by a give name.
   close                close a spider by a give name.
   restart              restart a stopped spider by a give name.
   resume               resume a paused spider by a give name.
   quit                 quit the Spider-Client.
   help                 show all the available commands usage.

舉例,假設當前爬蟲惟一標識名稱爲lianjia,則能夠經過:

$amipy> pause lianjia

來暫停爬蟲lianjia的爬取進度,在爬蟲將當前請求隊列清空後會一直暫停,直到收到Telnet端發出的其餘命令。恢復爬蟲使用:

$amipy> resume lianjia

查看當前項目下全部爬蟲:

$amipy> list

詳細查看則使用:

$amipy> show spiders

開啓關閉Telnet在項目的配置文件settings.py中設置SPIDER_SERVER_ENABLE。

爬取去重

Amipy的爬取去重能夠分爲兩種:

  • url去重
  • 網頁內容正文去重

二者皆使用了布隆過濾器去重,對於url去重,則是使用url+method+params+data的方式生成摘要進行布隆過濾器去重。
對於網頁正文去重則是按照配置文件指定的正文檢測參數來檢測每一個網頁的正文內容生成摘要存進布隆過濾器,能夠在爬蟲的配置文件
settings.py中對如下幾項進行配置來檢測網頁內容正文:

# 網頁內容剔除掉哪些標籤後再識別正文
BLOOMFILTER_HTML_EXTRACTS = ['script','style','head']
# 容許一個正文內容塊中至多空行數
BLOOMFILTER_HTML_GAP = 3
# 連續多少行有正文則認爲是一個正文塊
BLOOMFILTER_HTML_THRESHOLD = 5
# 每一行正文的字密度
BLOOMFILTER_HTML_DENSITY =45

上面兩種是默認的去重方式,還能夠指定請求返回的網頁內容的某一部分做爲響應指紋來進行鍼對性的去重。
若是想要本身指定哪一個響應內容部分做爲去重的指紋,能夠在將請求Request送進協程隊列時指定指紋函數,如:

...
    def parse(self,response):
        ...
        send(Request(self,url,fingerprint=self.fingerprint))
        ...
    
    def fingerprint(self,response):
        ...
        # 返回須要做爲指紋的文本字符等
        return something

例子

1. 使用Amipy建立鏈家網爬蟲(LianJiaSpider)

爬蟲目的:爬取鏈家網上北京當前最新的租房信息,包含「價格」,「房屋基本信息」、「配套設施」、「房源描述」、「聯繫經紀人」、「地址和交通」存入MongoDB數據庫中
  • 建立項目

進入到D:LianJia路徑,建立Amipy項目LJproject:

D:\LianJia> amipy cproject LJproject
  • 建立爬蟲

進入到項目路徑D:LianJiaLJproject,建立Amipy爬蟲lianjia:

D:\LianJia\LJproject> amipy cspider lianjia
  • 編寫數據庫模型

打開D:LianJiaLJprojectspidersLianjiaitem.py,編寫數據保存模型:

#coding:utf-8

from amipy.BaseClass.orm import Model,Field

class LianJiaRenting(Model):
    price = Field('價格')
    infos = Field('房屋基本信息')
    facility = Field('配套設施')
    desc = Field('房源描述')
    agent = Field('聯繫經紀人')
    addr = Field('地址與交通')
  • 設置數據庫鏈接

打開 D:LianJiaLJprojectspidersLianjiasettings.py,找到MongoDB數據庫鏈接設置,進行設置:

# MongoDB settings for data saving.
DATABASE_SETTINGS = {
    'host':'127.0.0.1',
    'port':27017,
    'user':'',
    'password':'',
    'database':'LianJiaDB',
}

要先確保系統安裝好MongoDB數據庫並已經開啓了服務。

  • 編寫爬蟲腳本

打開 D:LianJiaLJprojectspidersLianjiaspider.py,編寫爬蟲採集腳本:

import amipy,re
from amipy import send,Request,Url
from bs4 import BeautifulSoup as bs 

class LianjiaSpider(amipy.Spider):

   name = 'lianjia'
   # 設置爬取初始連接
   urls = ['https://bj.lianjia.com/zufang/']
   # 設置爬蟲白名單,只容許爬取匹配的連接
   whitelist = [
       Url(re='https://bj.lianjia.com/zufang/.*'),
   ]
   # 自定義的屬性
   host ='https://bj.lianjia.com'
   page = 1
   
   # 請求成功回調函數
   def parse(self,response):
       soup = bs(response.text(),'lxml')
       item_list = soup('div',class_='content__list--item')
       for i in item_list:
       # 獲取詳情頁連接 併發送至爬蟲請求隊列
           url = self.host+i.a['href']
           send(Request(self,url,callback=self.details))
       # 添加下一頁
       totalpage = soup('div',class_='content__pg')[0]['data-totalpage']
       if self.page>=int(totalpage):
           return
       self.page +=1
       send(Request(self,self.host+'/zufang/pg{}/'.format(self.page)))
       
   def details(self,response):
       infos = {}
       agent = {}
       facility = []
       soup = bs(response.text(),'lxml')
       infos_li = soup('div',class_='content__article__info')[0].ul('li')
       facility_li = soup('ul',class_='content__article__info2')[0]('li')
       agent_ul = soup('ul',id='agentList')[0]
       addr_li = soup('div',id='around')[0].ul.li
       desc_li = soup('div',id='desc')[0].li
       desc_li.div.extract()
       desc = desc_li.p['data-desc'] if desc_li.p else ''
       for i in infos_li:
           text = i.text
           if ':' in text:
               infos.update({text.split(':')[0]:text.split(':')[1]})
       for i in facility_li[1:]:
           if '_no' not in i['class'][-2]:
               facility.append(i.text)
       for div in agent_ul('div',class_='desc'):
           name = div.a.text
           phone = div('div',class_='phone')[0].text
           agent[name]=phone
       # 數據模型對應並保存
       self.item.desc = desc
       self.item.addr = re.sub(r'[\r\n ]','',addr_li.text) if addr_li else ''
       self.item.price = soup('p',class_='content__aside--title')[0].text
       self.item.infos = infos
       self.item.agent = agent
       self.item.facility = facility
       self.item.save()
若是在爬蟲配置文件settings.py中設置遵照目標網站機器人協議可能會被禁止採集,能夠自行關閉設置。
另外,開啓網頁內容類似過濾 BLOOMFILTER_HTML_ON可能會使爬取的結果數較少,爬蟲只會採集類似度不一樣的網頁內容的連接,
若是須要大批量採集,而網頁正文較少的,能夠關閉這個設置。

代碼比較粗糙,但能夠知道Amipy爬蟲基本的實現流程。

  • 運行爬蟲

在項目根路徑下,輸入:

D:\LianJia\LJproject> amipy runspider
  • 查看數據庫

進入MongoDB數據庫:能夠看到在數據庫‘LianJiaDB’下的集合「LianJiaRenting」中已經保存有咱們爬取的數據,格式以下:

{
    "_id" : ObjectId("5c6541b065b2fd1cf002c565"),
    "價格" : "7500元/月 (季付價)",
    "房屋基本信息" : {
        "發佈" : "20天前",
        "入住" : "隨時入住",
        "租期" : "2~3年",
        "看房" : "暫無數據",
        "樓層" : "中樓層/6層",
        "電梯" : "無",
        "車位" : "暫無數據",
        "用水" : "民水",
        "用電" : "民電",
        "燃氣" : "有",
        "採暖" : "集中供暖"
    },
    "配套設施" : [ 
        "電視", 
        "冰箱", 
        "洗衣機", 
        "空調", 
        "熱水器", 
        "牀", 
        "暖氣", 
        "寬帶", 
        "衣櫃", 
        "自然氣"
    ],
    "房源描述" : "【交通出行】 小區門口爲八里莊南里公交車站,75,675等多路公交通過。地鐵6號線十里堡站D口,距離地鐵口400米,交通十分方便,便於出行。<br />\n【周邊配套】 此房位置棒棒噠,有建設銀行,中國銀行,交通銀行,郵政儲蓄,果多美水果超市,購物,金旭菜市場,娛樂,休閒,便利。旁邊首航超市,姥姥家春餅,味多美蛋糕店,生活方便。<br />\n【小區介紹】 該小區中此樓是1981建成,安全溫馨,小區內主力樓盤爲6層板樓,先後無遮擋,此樓是多見的板樓,樓層高視野好。<br />\n",
    "聯繫經紀人" : {
        "宋玉恆" : "4000124028轉7907"
    },
    "地址與交通" : "距離6號線-十里堡192m"
}
  • 查看當前爬取進度

新開一個DOS端口,輸入:

> telnet 127.0.0.1 2232

進行Telnet鏈接,可使用命令操做查看當前爬蟲的爬取狀態。例如使用echo命令:

$amipy> echo lianjia

能夠查看當前爬蟲的狀態:

----------------Spider-lianjia-------------------
- Name:lianjia  Status:RUNNING
- Class:LianjiaSpider
- Success:25    Fail:0     Exception:0
- Priority:0
- SeedUrls:['https://bj.lianjia.com/zufang/']
- Path:D:\LianJia\LJproject\spiders\Lianjia
- Session:<aiohttp.client.ClientSession object at  0x000000000386FE10>
- StartAt:Thu Feb 14 20:30:21 2019
- PausedAt:None
- ResumeAt:None
- StopAt:None
- RestartAt:None
- CloseAt:None
--------------------------------------------------
相關文章
相關標籤/搜索