一篇文章搞定 Scrapy 爬蟲框架

Scrapy框架
css


Scrapy是用Python實現的一個爲了爬取網站數據,提取結構性數據而編寫的應用框架。能夠應用在包括數據挖掘、信息處理或存儲歷史數據等一系列的程序中。html


Scrapy使用Twisted基於事件的高效異步網絡框架來處理網絡通訊,能夠加快下載速度,不用本身去實現異步框架,而且包含了各類中間件接口,能夠靈活的完成各類需求。python


Scrapy架構正則表達式


圖片


Scrapy Engineshell


  • 引擎,負責控制數據流在系統中全部組件中流動,並在相應動做發生時觸發事件。此組件至關於爬蟲的「大腦」,是整個爬蟲的調度中心數據庫


調度器(Scheduler)編程


  • 調度器接收從引擎發送過來的request,並將他們入隊,以便以後引擎請求他們時提供給引擎json

  • 初始的爬取URL和後續在頁面中獲取的待爬取的URL將放入調度器中,等待爬取。同時調度器會自動去除重複的URL(若是特定的URL不須要去重也能夠經過設置實現,如post請求的URL)windows


下載器(Downloader)api


  • 下載器負責獲取頁面數據並提供給引擎,然後提供給spider


Spiders爬蟲


Spider是編寫的類,做用以下:

  • Scrapy用戶編寫用於分析response並提取item(即獲取到的item)

  • 額外跟進的URL,將額外跟進的URL提交給引擎,加入到Scheduler調度器中。將每一個spider負責處理一個特定(或一些)網站


Item Pipeline


  • Item Pipeline負責處理被spider提取出來的item。典型的處理有清理、 驗證及持久化(例如存取到數據庫中)

  • 當頁面被爬蟲解析所需的數據存入Item後,將被髮送到項目管道(Pipeline),並通過設置好次序的pipeline程序處理這些數據,最後將存入本地文件或存入數據庫

  • 相似管道 $ ls | grep test 或者相似於Django 模板中的過濾器


如下是item pipeline的一些典型應用:

  • 清理HTML數據

  • 驗證爬取的數據(檢查item包含某些字段)

  • 查重(或丟棄)

  • 將爬取結果保存到數據庫中


下載器中間件(Downloader middlewares)


簡單講就是自定義擴展下載功能的組件。

  • 下載器中間件,是在引擎和下載器之間的特定鉤子(specific hook),處理它們之間的請求request和響應response。

    它提供了一個簡便的機制,經過插入自定義代碼來擴展Scrapy功能

  • 經過設置下載器中間件能夠實現爬蟲自動更換user-agent、IP等功能


Spider中間件(Spider middlewares)


Spider中間件,是在引擎和Spider之間的特定鉤子(specific hook),處理spider的輸入(response)和輸出(items或requests)。


也提供了一樣的簡便機制,經過插入自定義代碼來擴展Scrapy功能。


數據流(Data flow)


  1. 引擎打開一個網站(open a domain),找處處理該網站的Spider並向該spider請求第一個(批)要爬取的URL(s)

  2. 引擎從Spider中獲取到第一個要爬取的URL並加入到調度器(Scheduler)做爲請求以備調度

  3. 引擎向調度器請求下一個要爬取的URL

  4. 調度器返回下一個要爬取的URL給引擎,引擎將URL經過下載中間件並轉發給下載器(Downloader)

  5. 一旦頁面下載完畢,下載器生成一個該頁面的Response,並將其經過下載中間件發送給引擎

  6. 引擎從下載器中接收到Response,而後經過Spider中間件發送給Spider處理

  7. Spider處理Response並返回提取到的Item及(跟進的)新的Request給引擎

  8. 引擎將Spider返回的Item交給Item Pipeline,將Spider返回的Request交給調度器

  9. (從第二步)重複執行,直到調度器中沒有待處理的request,引擎關閉


注意:

只有當調度器中沒有任何request了,整個程序纔會中止執行。若是有下載失敗的URL,會從新下載


安裝scrapy


  • 安裝wheel支持

    $ pip install wheel

  • 安裝scrapy框架

    $ pip install scrapy

  • window下,爲了不windows編譯安裝twisted依賴,安裝下面的二進制包

    $ pip install Twisted-18.4.0-cp35-cp35m-win_amd64.whl


windows下出現以下問題:

copying src\twisted\words\xish\xpathparser.g -> build\lib.win-amd64-3.5\twisted\words\xish running build_ext building 'twisted.test.raiser' extension error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools": http://landinghub.visualstudio.com/visual-cpp-build-tools解決方案是,下載編譯好的twisted,https://www.lfd.uci.edu/~gohlke/pythonlibs/#twistedpython3.5 下載 Twisted-18.4.0-cp35-cp35m-win_amd64.whlpython3.6 下載 Twisted-18.4.0-cp36-cp36m-win_amd64.whl
安裝twisted$ pip install Twisted-18.4.0-cp35-cp35m-win_amd64.whl以後在安裝scrapy就沒有什麼問題了


安裝好,使用scrapy命令看看


> scrapyScrapy 1.5.0 - no active project
Usage:  scrapy <command> [options] [args]Available commands:  bench Run    quick benchmark test  check       Check spider contracts  crawl       Run a spider  edit       Edit spider  fetch       Fetch a URL using the Scrapy downloader  genspider     Generate new spider using pre-defined templates  list       List available spiders  parse       Parse URL (using its spider) and print the results  runspider     Run a self-contained spider (without creating a project)  settings     Get settings values  shell       Interactive scraping console  startproject   Create new project  version     Print Scrapy version  view       Open URL in browser, as seen by Scrapy


Scrapy開發


項目編寫流程


  1. 建立項目

    使用 scrapy startproject proname 建立一個scrapy項目

    scrapy startproject <project_name> [project_dir]

  2. 編寫item

    在items.py中編寫Item類,明確從response中提取的item

  3. 編寫爬蟲

    編寫spiders/proname_spider.py,即爬取網站的spider並提取出item

  4. 編寫item pipeline

    item的處理,能夠存儲


1 建立項目


1.1 豆瓣書評爬取


標籤爲「編程」,第一頁、第二頁連接:

https://book.douban.com/tag/%E7%BC%96%E7%A8%8B?start=0&type=T

https://book.douban.com/tag/%E7%BC%96%E7%A8%8B?start=20&type=T


隨便找一個目錄來建立項目,執行下面命令

$ scrapy startproject first .


會產生以下目錄和文件


first ├─ scrapy.cfg └─ first   ├─ items.py   ├─ middlewares.py   ├─ pipelines.py   ├─ settings.py   ├─ __init__.py   └─ spiders     └─ __init__.py


  • first:

    外部的first目錄是整個項目目錄,內部的first目錄是整個項目的全局目錄

  • scrapy.cfg:

    必須有的重要的項目的配置文件

  • first 項目目錄

  • __init__.py 必須有,包文件

  • items.py 定義Item類,從scrapy.Item繼承,裏面定義scrapy.Field類實例

  • pipelines.py 重要的是process_item()方法,處理item

  • settings.py:


  • BOT_NAME 爬蟲名

  • ROBOTSTXT_OBEY = True 是否聽從robots協議

  • USER_AGENT = '' 指定爬取時使用

  • CONCURRENT_REQEUST = 16 默認16個並行

  • DOWNLOAD_DELAY = 3 下載延時,通常要設置,不宜過快發起連續請求

  • COOKIES_ENABLED = False 缺省是啓用,通常須要登陸時才須要開啓cookie

  • SPIDER_MIDDLEWARES 爬蟲中間件

  • DOWNLOADER_MIDDLEWARES 下載中間件 

    • 'firstscrapy.pipelines.FirstscrapyPipeline': 300item交給哪個管道處理,300 越小優先級越高

  • ITEM_PIPELINES 管道配置

    • 'first.middlewares.FirstDownloaderMiddleware': 543543 越小優先級越高

spiders目錄

__init__.py 必須有,能夠在這裏寫爬蟲類,也能夠寫爬蟲子模塊


# first/settings.py參考BOT_NAME = 'first'SPIDER_MODULES = ['first.spiders']NEWSPIDER_MODULE = 'first.spiders'
USER_AGENT = "Mozilla/5.0 (Windows NT 6.1)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36"ROBOTSTXT_OBEY = False
DOWNLOAD_DELAY = 3
# Disable cookies (enabled by default)COOKIES_ENABLED = False


注意必定要更改User-Agent,不然訪問 https://book.douban.com/ 會返回403


2 編寫Item


在items.py中編寫


import scrapy
class BookItem(scrapy.Item):  title = scrapy.Field() # 書名  rate = scrapy.Field() # 評分


3 編寫爬蟲


爲爬取豆瓣書評編寫爬蟲類,在spiders目錄下:

  • 編寫的爬蟲類須要繼承自scrapy.Spider,在這個類中定義爬蟲名、爬取範圍、其實地址等

    • 在scrapy.Spider中parse方法未實現,因此子類應該實現parse方法。該方法傳入response對象

# scrapy源碼中class Spider():  def parse(self, response): # 解析返回的內容    raise NotImplementedError


爬取讀書頻道,tag爲「編程」的書名和評分:

https://book.douban.com/tag/%E7%BC%96%E7%A8%8B?start=20&type=T


使用模板建立spider, $ scrapy genspider -t basic book douban.com


import scrapy
class BookSpider(scrapy.Spider): # BookSpider  name = 'doubanbook' # 爬蟲名,可修改,重要  allowed_domains = ['douban.com'] # 爬蟲爬取範圍  url = 'https://book.douban.com/tag/%E7%BC%96%E7%A8%8B?start=0&type=T'  start_urls = [url] # 起始URL  # 下載器獲取了WEB Server的response就好了,parse就是解析響應的內容  def parse(self, response):    print(type(response), '~~~~~~~~~') #scrapy.http.response.html.HtmlResponse    print(response)    print('-' * 30)


使用crawl爬取子命令


$ scrapy list$ scrapy crawl -hscrapy crawl [options] <spider>
指定爬蟲名稱開始爬取$ scrapy crawl doubanbook
能夠不打印日誌$ scrapy crawl doubanbook --nolog


若是在windows下運行發生twisted的異常 ModuleNotFoundError: No module named 'win32api' ,請安裝 $ pip install pywin32。


response是服務器端HTTP響應,它是scrapy.http.response.html.HtmlResponse類。


由此,修改代碼以下

import scrapyfrom scrapy.http.response.html import HtmlResponse
class BookSpider(scrapy.Spider): # BookSpider  name = 'doubanbook' # 爬蟲名  allowed_domains = ['douban.com'] # 爬蟲爬取範圍  url = 'https://book.douban.com/tag/%E7%BC%96%E7%A8%8B?start=0&type=T'  start_urls = [url] # 起始URL  # 下載器獲取了WEB Server的response就好了,parse就是解析響應的內容  def parse(self, response:HtmlResponse):    print(type(response)) #scrapy.http.response.html.HtmlResponse    print('-'*30)    print(type(response.text), type(response.body))    print('-'*30)    print(response.encoding)    with open('o:/testbook.html', 'w', encoding='utf-8') as f:      try:        f.write(response.text)        f.flush()      except Exception as e:        print(e)


3.1 解析HTML


爬蟲得到的內容response對象,可使用解析庫來解析。


scrapy包裝了lxml,父類TextResponse類也提供了xpath方法和css方法,能夠混合使用這兩套接口解析HTML。


選擇器參考:

 https://scrapy-chs.readthedocs.io/zh_CN/0.24/topics/selectors.html#id3


import scrapyfrom scrapy.http.response.html import HtmlResponse
response = HtmlResponse('file:///O:/testbook.html', encoding='utf-8') # 構造對象
with open('o:/testbook.html', encoding='utf8') as f:  response._set_body(f.read()) # 填充數據  #print(response.text)  # 獲取全部標題及評分  # xpath解析  subjects = response.xpath('//li[@class="subject-item"]')  for subject in subjects:    title = subject.xpath('.//h2/a/text()').extract() # list    print(title[0].strip())    rate = subject.xpath('.//span[@class="rating_nums"]/text()').extract()    print(rate[0].strip())
 print('-'*30)  # css解析  subjects = response.css('li.subject-item')  for subject in subjects:    title = subject.css('h2 a::text').extract()    print(title[0].strip())    rate = subject.css('span.rating_nums::text').extract()    print(rate[0].strip())  print('-'*30)  # xpath和css混合使用、正則表達式匹配  subjects = response.css('li.subject-item')  for subject in subjects:  # 提取連接    href =subject.xpath('.//h2').css('a::attr(href)').extract()    print(href[0])    # 使用正則表達式    id = subject.xpath('.//h2/a/@href').re(r'\d*99\d*')    if id:      print(id[0])    # 要求顯示9分以上數據    rate = subject.xpath('.//span[@class="rating_nums"]/text()').re(r'^9.*')    # rate = subject.css('span.rating_nums::text').re(r'^9\..*')    if rate:      print(rate)


3.2 item封裝數據


# spiders/bookspider.pyimport scrapyfrom scrapy.http.response.html import HtmlResponsefrom ..items import BookItem
class BookSpider(scrapy.Spider): # BookSpider  name = 'doubanbook' # 爬蟲名  allowed_domains = ['douban.com'] # 爬蟲爬取範圍  url = 'https://book.douban.com/tag/%E7%BC%96%E7%A8%8B?start=0&type=T'  start_urls = [url] # 起始URL  # 下載器獲取了WEB Server的response就好了,parse就是解析響應的內容  def parse(self, response:HtmlResponse):    items = []    # xpath解析    subjects = response.xpath('//li[@class="subject-item"]')    for subject in subjects:      title = subject.xpath('.//h2/a/text()').extract()          rate = subject.xpath('.//span[@class="rating_nums"]/text()').extract_first()          item = BookItem()          item['title'] = title[0].strip()          item['rate'] = rate.strip()          items.append(item)    print(items)    return items # 必定要return,不然保存不下來# 使用命令保存return的數據# scrapy crawl -h# --output=FILE, -o FILE dump scraped items into FILE (use - for stdout)# 文件擴展名支持'json', 'jsonlines', 'jl', 'csv', 'xml', 'marshal', 'pickle'# scrapy crawl doubanbook -o dbbooks.json


獲得下圖數


注意上圖的數據已是unicode字符,漢字的unicode表達。


4 pipeline處理


將bookspider.py中BookSpider改爲生成器,只須要把 return items 改形成 yield item ,即由產生一個列表變成yield一個個item

腳手架幫咱們建立了一個pipelines.py文件和一個類


4.1 開啓pipeline


# Configure item pipelines# See https://doc.scrapy.org/en/latest/topics/item-pipeline.htmlITEM_PIPELINES = {  'first.pipelines.FirstPipeline': 300,}


整數300表示優先級,越小越高。


取值範圍爲0-1000


4.2經常使用方法


class FirstPipeline(object):   def __init__(self): # 全局設置     print('~~~~~~~~~~ init ~~~~~~~~~~~~')   def open_spider(self, spider): # 當某spider開啓時調用     print(spider,'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~')   def process_item(self, item, spider):     # item 獲取的item;spider 獲取該item的spider     return item 
 def close_spider(self, spider): # 當某spider關閉時調用    print(spider,'========================================')


需求


經過pipeline將爬取的數據存入json文件中


# spider/bookspider.pyimport scrapyfrom scrapy.http.response.html import HtmlResponsefrom ..items import BookItem
class BookSpider(scrapy.Spider): # BookSpider  name = 'doubanbook' # 爬蟲名  allowed_domains = ['douban.com'] # 爬蟲爬取範圍  url = 'https://book.douban.com/tag/%E7%BC%96%E7%A8%8B?start=0&type=T'  start_urls = [url] # 起始URL  # spider上自定義配置信息  custom_settings = {    'filename' : 'o:/books.json'  }  # 下載器獲取了WEB Server的response就好了,parse就是解析響應的內容  def parse(self, response:HtmlResponse):    #items = []    # xpath解析    subjects = response.xpath('//li[@class="subject-item"]')    for subject in subjects:      title = subject.xpath('.//h2/a/text()').extract()      rate =subject.xpath('.//span[@class="rating_nums"]/text()').extract_first()      item = BookItem()      item['title'] = title[0].strip()      item['rate'] = rate.strip()      #items.append(item)      yield item    #return items
# pipelines.pyimport simplejson as json
class FirstPipeline(object):  def __init__(self): # 全局設置    print('~~~~~~~~~~ init ~~~~~~~~~~~~')  def open_spider(self, spider): # 當某spider開啓時調用    print('{} ~~~~~~~~~~~~~~~~~~~~'.format(spider))    print(spider.settings.get('filename'))    self.file = open(spider.settings['filename'], 'w', encoding='utf-8')    self.file.write('[\n')  def process_item(self, item, spider):    # item 獲取的item;spider 獲取該item的spider    self.file.write(json.dumps(dict(item)) + ',\n')    return item  def close_spider(self, spider): # 當某spider關閉時調用    self.file.write(']')    self.file.close()    print('{} ======================='.format(spider))    print('-'*30)


5 url提取


若是要爬取下一頁內容,能夠本身分析每一頁的頁碼變化,也能夠經過提取分頁欄的連接


# spider/bookspider.pyimport scrapyfrom scrapy.http.response.html import HtmlResponsefrom ..items import BookItem
class BookSpider(scrapy.Spider): # BookSpider  name = 'doubanbook' # 爬蟲名  allowed_domains = ['douban.com'] # 爬蟲爬取範圍  url = 'https://book.douban.com/tag/%E7%BC%96%E7%A8%8B?start=0&type=T'  start_urls = [url] # 起始URL  # spider上自定義配置信息  custom_settings = {      'filename' : 'o:/books.json'   }  # 下載器獲取了WEB Server的response就好了,parse就是解析響應的內容    def parse(self, response:HtmlResponse):    #items = []      # xpath解析      # 獲取下一頁,只是測試,因此使用re來控制頁碼    print('-' * 30)    urls = response.xpath('//div[@class="paginator"]/span[@class="next"]/a/@href').re(              r'.*start=[24]\d[^\d].*')    print(urls)    print('-' * 30)    yield from (scrapy.Request(response.urljoin(url)) for url in urls)      print('++++++++++++++++++++++++')    subjects = response.xpath('//li[@class="subject-item"]')    for subject in subjects:    # 解決圖書副標題拼接      title = "".join(map(lambda x:x.strip(), subject.xpath('.//h2/a//text()').extract()))      rate = subject.xpath('.//span[@class="rating_nums"]/text()').extract_first()      #print(rate) # 有的沒有評分,要注意可能返回None      item = BookItem()      item['title'] = title      item['rate'] = rate      #items.append(item)      yield item    #return items


Linux雲計算課程全新升級,雲計算+安全+DevOps上線,面授班12月9日開班,120天衝擊年薪30萬,改變速約~~~~

相關文章
相關標籤/搜索