摘要:組件引擎負責控制數據流在系統中所有組件中流動,并在相應動作發生時觸發事件。下載器下載器負責獲取頁面數據并提供給引擎,而后提供給。下載器中間件下載器中間件是在引擎及下載器之間的特定鉤子,處理傳遞給引擎的。
一、Scrapy框架簡介Scrapy 是用Python實現一個為爬取網站數據、提取結構性數據而編寫的應用框架。
Scrapy是一個為了爬取網站數據,提取結構性數據而編寫的應用框架。 可以應用在包括數據挖掘,信息處理或存儲歷史數據等一系列的程序中。
其最初是為了 頁面抓取 (更確切來說, 網絡抓取 )所設計的, 也可以應用在獲取API所返回的數據(例如 Amazon Associates Web Services ) 或者通用的網絡爬蟲。
二、架構流程圖接下來的圖表展現了Scrapy的架構,包括組件及在系統中發生的數據流的概覽(綠色箭頭所示)。 下面對每個組件都做了簡單介紹,并給出了詳細內容的鏈接。數據流如下所描述。
1、組件 Scrapy Engine引擎負責控制數據流在系統中所有組件中流動,并在相應動作發生時觸發事件。 詳細內容查看下面的數據流(Data Flow)部分。
調度器(Scheduler)調度器從引擎接受request并將他們入隊,以便之后引擎請求他們時提供給引擎。
下載器(Downloader)下載器負責獲取頁面數據并提供給引擎,而后提供給spider。
SpidersSpider是Scrapy用戶編寫用于分析response并提取item(即獲取到的item)或額外跟進的URL的類。 每個spider負責處理一個特定(或一些)網站。 更多內容請看 Spiders 。
Item PipelineItem Pipeline負責處理被spider提取出來的item。典型的處理有清理、 驗證及持久化(例如存取到數據庫中)。 更多內容查看 Item Pipeline 。
下載器中間件(Downloader middlewares)下載器中間件是在引擎及下載器之間的特定鉤子(specific hook),處理Downloader傳遞給引擎的response。 其提供了一個簡便的機制,通過插入自定義代碼來擴展Scrapy功能。更多內容請看 下載器中間件(Downloader Middleware) 。
Spider中間件(Spider middlewares)Spider中間件是在引擎及Spider之間的特定鉤子(specific hook),處理spider的輸入(response)和輸出(items及requests)。 其提供了一個簡便的機制,通過插入自定義代碼來擴展Scrapy功能。更多內容請看 Spider中間件(Middleware) 。
2、數據流(Data flow)Scrapy中的數據流由執行引擎控制,其過程如下:
引擎打開一個網站(open a domain),找到處理該網站的Spider并向該spider請求第一個要爬取的URL(s)。
引擎從Spider中獲取到第一個要爬取的URL并在調度器(Scheduler)以Request調度。
引擎向調度器請求下一個要爬取的URL。
調度器返回下一個要爬取的URL給引擎,引擎將URL通過下載中間件(請求(request)方向)轉發給下載器(Downloader)。
一旦頁面下載完畢,下載器生成一個該頁面的Response,并將其通過下載中間件(返回(response)方向)發送給引擎。
引擎從下載器中接收到Response并通過Spider中間件(輸入方向)發送給Spider處理。
Spider處理Response并返回爬取到的Item及(跟進的)新的Request給引擎。
引擎將(Spider返回的)爬取到的Item給Item Pipeline,將(Spider返回的)Request給調度器。
(從第二步)重復直到調度器中沒有更多地request,引擎關閉該網站。
3、事件驅動網絡(Event-driven networking)Scrapy基于事件驅動網絡框架 Twisted 編寫。因此,Scrapy基于并發性考慮由非阻塞(即異步)的實現。
關于異步編程及Twisted更多的內容請查看下列鏈接:
三、4步制作爬蟲新建項目(scrapy startproject xxx):新建一個新的爬蟲項目
明確目標(編寫items.py):明確你想要抓取的目標
制作爬蟲(spiders/xxsp der.py):制作爬蟲開始爬取網頁
存儲內容(pipelines.py):設計管道存儲爬取內容
四、安裝框架這里我們使用 conda 來進行安裝:
conda install scrapy
或者使用 pip 進行安裝:
pip install scrapy
查看安裝:
? spider scrapy -h Scrapy 1.4.0 - no active project Usage: scrapy1.創建項目[options] [args] Available commands: bench Run quick benchmark test fetch Fetch a URL using the Scrapy downloader genspider Generate new spider using pre-defined templates 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 [ more ] More commands available when run from project directory Use "scrapy -h" to see more info about a command
? spider scrapy startproject SF New Scrapy project "SF", using template directory "/Users/kaiyiwang/anaconda2/lib/python2.7/site-packages/scrapy/templates/project", created in: /Users/kaiyiwang/Code/python/spider/SF You can start your first spider with: cd SF scrapy genspider example example.com ? spider
使用 tree 命令可以查看項目結構:
? SF tree . ├── SF │?? ├── __init__.py │?? ├── items.py │?? ├── middlewares.py │?? ├── pipelines.py │?? ├── settings.py │?? └── spiders │?? └── __init__.py └── scrapy.cfg2.在spiders 目錄下創建模板
? spiders scrapy genspider sf "https://segmentfault.com" Created spider "sf" using template "basic" in module: SF.spiders.sf ? spiders
這樣,就生成了一個項目文件 sf.py
# -*- coding: utf-8 -*- import scrapy from SF.items import SfItem class SfSpider(scrapy.Spider): name = "sf" allowed_domains = ["https://segmentfault.com"] start_urls = ["https://segmentfault.com/"] def parse(self, response): # print response.body # pass node_list = response.xpath("http://h2[@class="title"]") # 用來存儲所有的item字段的 # items = [] for node in node_list: # 創建item字段對象,用來存儲信息 item = SfItem() # .extract() 將xpath對象轉換為 Unicode字符串 title = node.xpath("./a/text()").extract() item["title"] = title[0] # 返回抓取到的item數據,給管道文件處理,同時還回來繼續執行后邊的代碼 yield.item #return item #return scrapy.Request(url) #items.append(item)
命令:
# 測試爬蟲是否正常, sf為爬蟲的名稱 ? scrapy check sf # 運行爬蟲 ? scrapy crawl sf3.item pipeline
當 item 在Spider中被收集之后,它將會被傳遞到 item Pipeline, 這些 item Pipeline 組件按定義的順序處理 item.
每個 Item Pipeline 都是實現了簡單方法的Python 類,比如決定此Item是丟棄或存儲,以下是 item pipeline 的一些典型應用:
驗證爬取得數據(檢查item包含某些字段,比如說name字段)
查重(并丟棄)
將爬取結果保存到文件或者數據庫總(數據持久化)
編寫 item pipeline
編寫 item pipeline 很簡單,item pipeline 組件是一個獨立的Python類,其中 process_item()方法必須實現。
from scrapy.exceptions import DropItem class PricePipeline(object): vat_factor = 1.15 def process_item(self, item, spider): if item["price"]: if item["price_excludes_vat"]: item["price"] = item["price"] * self.vat_factor return item else: raise DropItem("Missing price in %s" % item)4.選擇器(Selectors)
當抓取網頁時,你做的最常見的任務是從HTML源碼中提取數據。
Selector 有四個基本的方法,最常用的還是Xpath
xpath():傳入xpath表達式,返回該表達式所對應的所有節點的selector list 列表。
extract(): 序列化該節點為Unicode字符串并返回list
css():傳入CSS表達式,返回該表達式所對應的所有節點的selector list 列表,語法同 BeautifulSoup4
re():根據傳入的正則表達式對數據進行提取,返回Unicode 字符串list 列表
Scrapy提取數據有自己的一套機制。它們被稱作選擇器(seletors),因為他們通過特定的 XPath 或者 CSS 表達式來“選擇” HTML文件中的某個部分。
XPath 是一門用來在XML文件中選擇節點的語言,也可以用在HTML上。 CSS 是一門將HTML文檔樣式化的語言。選擇器由它定義,并與特定的HTML元素的樣式相關連。
Scrapy選擇器構建于 lxml 庫之上,這意味著它們在速度和解析準確性上非常相似。
XPath表達式的例子:
/html/head/title: 選擇文檔中標簽內的元素 /html/head/title/text(): 選擇上面提到的 元素的問題 //td: 選擇所有的 元素 //div[@class="mine"]:選擇所有具有 class="mine" 屬性的 div 元素 更多XPath 語法總結請看這里。
五、爬取招聘信息 1.爬取騰訊招聘信息爬取的地址:http://hr.tencent.com/positio...
1.1 創建項目> scrapy startproject Tencent You can start your first spider with: cd Tencent scrapy genspider example example.com需要抓取網頁的元素:
我們需要爬取以下信息:
職位名:positionName
職位鏈接:positionLink
職位類型:positionType
職位人數:positionNumber
工作地點:workLocation
發布時點:publishTime在 items.py 文件中定義爬取的字段:
# -*- coding: utf-8 -*- # Define here the models for your scraped items # # See documentation in: # http://doc.scrapy.org/en/latest/topics/items.html import scrapy # 定義字段 class TencentItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() # 職位名 positionName = scrapy.Field() # 職位鏈接 positionLink = scrapy.Field() # 職位類型 positionType = scrapy.Field() # 職位人數 positionNumber = scrapy.Field() # 工作地點 workLocation = scrapy.Field() # 發布時點 publishTime = scrapy.Field() pass1.2 寫spider爬蟲使用命令創建
? Tencent scrapy genspider tencent "tencent.com" Created spider "tencent" using template "basic" in module: Tencent.spiders.tencent生成的 spider 在當前目錄下的 spiders/tencent.py
? Tencent tree . ├── __init__.py ├── __init__.pyc ├── items.py ├── middlewares.py ├── pipelines.py ├── settings.py ├── settings.pyc └── spiders ├── __init__.py ├── __init__.pyc └── tencent.py我們可以看下生成的這個初始化文件 tencent.py
# -*- coding: utf-8 -*- import scrapy class TencentSpider(scrapy.Spider): name = "tencent" allowed_domains = ["tencent.com"] start_urls = ["http://tencent.com/"] def parse(self, response): pass對初識文件tencent.py進行修改:
# -*- coding: utf-8 -*- import scrapy from Tencent.items import TencentItem class TencentSpider(scrapy.Spider): name = "tencent" allowed_domains = ["tencent.com"] baseURL = "http://hr.tencent.com/position.php?&start=" offset = 0 # 偏移量 start_urls = [baseURL + str(offset)] def parse(self, response): # 請求響應 # node_list = response.xpath("http://tr[@class="even"] or //tr[@class="odd"]") node_list = response.xpath("http://tr[@class="even"] | //tr[@class="odd"]") for node in node_list: item = TencentItem() # 引入字段類 # 文本內容, 取列表的第一個元素[0], 并且將提取出來的Unicode編碼 轉為 utf-8 item["positionName"] = node.xpath("./td[1]/a/text()").extract()[0].encode("utf-8") item["positionLink"] = node.xpath("./td[1]/a/@href").extract()[0].encode("utf-8") # 鏈接屬性 item["positionType"] = node.xpath("./td[2]/text()").extract()[0].encode("utf-8") item["positionNumber"] = node.xpath("./td[3]/text()").extract()[0].encode("utf-8") item["workLocation"] = node.xpath("./td[4]/text()").extract()[0].encode("utf-8") item["publishTime"] = node.xpath("./td[5]/text()").extract()[0].encode("utf-8") # 返回給管道處理 yield item # 先爬 2000 頁數據 if self.offset < 2000: self.offset += 10 url = self.baseURL + self.offset yield scrapy.Request(url, callback = self.parse) #pass寫管道文件 pipelines.py:
# -*- coding: utf-8 -*- # Define your item pipelines here # # Don"t forget to add your pipeline to the ITEM_PIPELINES setting # See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html import json class TencentPipeline(object): def __init__(self): self.f = open("tencent.json", "w") # 所有的item使用共同的管道 def process_item(self, item, spider): content = json.dumps(dict(item), ensure_ascii = False) + ", " self.f.write(content) return item def close_spider(self, spider): self.f.close()管道寫好之后,在 settings.py 中啟用管道
# Configure item pipelines # See http://scrapy.readthedocs.org/en/latest/topics/item-pipeline.html ITEM_PIPELINES = { "Tencent.pipelines.TencentPipeline": 300, }運行:
> scrapy crawl tencent File "/Users/kaiyiwang/Code/python/spider/Tencent/Tencent/spiders/tencent.py", line 21, in parse item["positionName"] = node.xpath("./td[1]/a/text()").extract()[0].encode("utf-8") IndexError: list index out of range請求響應這里寫的有問題,Xpath或應該為這種寫法:
# 請求響應 # node_list = response.xpath("http://tr[@class="even"] or //tr[@class="odd"]") node_list = response.xpath("http://tr[@class="even"] | //tr[@class="odd"]")然后再執行命令:
> scrapy crawl tencent執行結果文件 tencent.json :
{"positionName": "23673-財經運營中心熱點運營組編輯", "publishTime": "2017-12-02", "positionLink": "position_detail.php?id=32718&keywords=&tid=0&lid=0", "positionType": "內容編輯類", "workLocation": "北京", "positionNumber": "1"}, {"positionName": "MIG03-騰訊地圖高級算法評測工程師(北京)", "publishTime": "2017-12-02", "positionLink": "position_detail.php?id=30276&keywords=&tid=0&lid=0", "positionType": "技術類", "workLocation": "北京", "positionNumber": "1"}, {"positionName": "MIG10-微回收渠道產品運營經理(深圳)", "publishTime": "2017-12-02", "positionLink": "position_detail.php?id=32720&keywords=&tid=0&lid=0", "positionType": "產品/項目類", "workLocation": "深圳", "positionNumber": "1"}, {"positionName": "MIG03-iOS測試開發工程師(北京)", "publishTime": "2017-12-02", "positionLink": "position_detail.php?id=32715&keywords=&tid=0&lid=0", "positionType": "技術類", "workLocation": "北京", "positionNumber": "1"}, {"positionName": "19332-高級PHP開發工程師(上海)", "publishTime": "2017-12-02", "positionLink": "position_detail.php?id=31967&keywords=&tid=0&lid=0", "positionType": "技術類", "workLocation": "上海", "positionNumber": "2"}1.3 通過下一頁爬取我們上邊是通過總的頁數來抓取每頁數據的,但是沒有考慮到每天的數據是變化的,所以,需要爬取的總頁數不能寫死,那該怎么判斷是否爬完了數據呢?其實很簡單,我們可以根據下一頁來爬取,只要下一頁沒有數據了,就說明數據已經爬完了。
我們通過 下一頁 看下最后一頁的特征:
下一頁的按鈕為灰色,并且鏈接為 class="noactive"屬性了,我們可以根據此特性來判斷是否到最后一頁了。
# 寫死總頁數,先爬 100 頁數據 """ if self.offset < 100: self.offset += 10 url = self.baseURL + str(self.offset) yield scrapy.Request(url, callback = self.parse) """ # 使用下一頁爬取數據 if len(response.xpath("http://a[@class="noactive" and @id="next"]")) == 0: url = response.xpath("http://a[@id="next"]/@href").extract()[0] yield scrapy.Request("http://hr.tencent.com/" + url, callback = self.parse)修改后的tencent.py文件:
# -*- coding: utf-8 -*- import scrapy from Tencent.items import TencentItem class TencentSpider(scrapy.Spider): # 爬蟲名 name = "tencent" # 爬蟲爬取數據的域范圍 allowed_domains = ["tencent.com"] # 1.需要拼接的URL baseURL = "http://hr.tencent.com/position.php?&start=" # 需要拼接的URL地址的偏移量 offset = 0 # 偏移量 # 爬蟲啟動時,讀取的URL地址列表 start_urls = [baseURL + str(offset)] # 用來處理response def parse(self, response): # 提取每個response的數據 node_list = response.xpath("http://tr[@class="even"] | //tr[@class="odd"]") for node in node_list: # 構建item對象,用來保存數據 item = TencentItem() # 文本內容, 取列表的第一個元素[0], 并且將提取出來的Unicode編碼 轉為 utf-8 print node.xpath("./td[1]/a/text()").extract() item["positionName"] = node.xpath("./td[1]/a/text()").extract()[0].encode("utf-8") item["positionLink"] = node.xpath("./td[1]/a/@href").extract()[0].encode("utf-8") # 鏈接屬性 # 進行是否為空判斷 if len(node.xpath("./td[2]/text()")): item["positionType"] = node.xpath("./td[2]/text()").extract()[0].encode("utf-8") else: item["positionType"] = "" item["positionNumber"] = node.xpath("./td[3]/text()").extract()[0].encode("utf-8") item["workLocation"] = node.xpath("./td[4]/text()").extract()[0].encode("utf-8") item["publishTime"] = node.xpath("./td[5]/text()").extract()[0].encode("utf-8") # yield的重要性,是返回數據后還能回來接著執行代碼,返回給管道處理,如果為return 整個函數都退出了 yield item # 第一種寫法:拼接URL,適用場景:頁面沒有可以點擊的請求鏈接,必須通過拼接URL才能獲取響應 """ if self.offset < 100: self.offset += 10 url = self.baseURL + str(self.offset) yield scrapy.Request(url, callback = self.parse) """ # 第二種寫法:直接從response獲取需要爬取的連接,并發送請求處理,直到連接全部提取完(使用下一頁爬取數據) if len(response.xpath("http://a[@class="noactive" and @id="next"]")) == 0: url = response.xpath("http://a[@id="next"]/@href").extract()[0] yield scrapy.Request("http://hr.tencent.com/" + url, callback = self.parse) #passOK,通過 根據下一頁我們成功爬完招聘信息的所有數據。
1.4 小結爬蟲步驟:
1.創建項目 scrapy project XXX
2.scarpy genspider xxx "http://www.xxx.com"
3.編寫 items.py, 明確需要提取的數據
4.編寫 spiders/xxx.py, 編寫爬蟲文件,處理請求和響應,以及提取數據(yield item)
5.編寫 pipelines.py, 編寫管道文件,處理spider返回item數據,比如本地數據持久化,寫文件或存到表中。
6.編寫 settings.py,啟動管道組件ITEM_PIPELINES,以及其他相關設置
7.執行爬蟲 scrapy crawl xxx
有時候被爬取的網站可能做了很多限制,所以,我們請求時可以添加請求報頭,scrapy 給我們提供了一個很方便的報頭配置的地方,settings.py 中,我們可以開啟:
# Crawl responsibly by identifying yourself (and your website) on the user-agent USER_AGENT = "Tencent (+http://www.yourdomain.com)" User-AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36" # Override the default request headers: DEFAULT_REQUEST_HEADERS = { "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en", }scrapy 最大的適用場景是爬取靜態頁面,性能非常強悍,但如果要爬取動態的json數據,那就沒必要了。
相關文章:
Scrapy入門教程
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/44467.html
相關文章
Python爬蟲之Scrapy學習(基礎篇)
摘要:下載器下載器負責獲取頁面數據并提供給引擎,而后提供給。下載器中間件下載器中間件是在引擎及下載器之間的特定鉤子,處理傳遞給引擎的。一旦頁面下載完畢,下載器生成一個該頁面的,并將其通過下載中間件返回方向發送給引擎。 作者:xiaoyu微信公眾號:Python數據科學知乎:Python數據分析師 在爬蟲的路上,學習scrapy是一個必不可少的環節。也許有好多朋友此時此刻也正在接觸并學習sc...
零基礎如何學爬蟲技術
摘要:楚江數據是專業的互聯網數據技術服務,現整理出零基礎如何學爬蟲技術以供學習,。本文來源知乎作者路人甲鏈接楚江數據提供網站數據采集和爬蟲軟件定制開發服務,服務范圍涵蓋社交網絡電子商務分類信息學術研究等。 楚江數據是專業的互聯網數據技術服務,現整理出零基礎如何學爬蟲技術以供學習,http://www.chujiangdata.com。 第一:Python爬蟲學習系列教程(來源于某博主:htt...
Python爬蟲框架scrapy入門指引
摘要:想爬點數據來玩玩,我想最方便的工具就是了。這框架把采集需要用到的功能全部封裝好了,只要寫寫采集規則其他的就交給框架去處理,非常方便,沒有之一,不接受反駁。首先,大概看下這門語言。如果文檔看不懂的話,推薦看看這個教程爬蟲教程 想爬點數據來玩玩, 我想最方便的工具就是Python scrapy了。 這框架把采集需要用到的功能全部封裝好了,只要寫寫采集規則,其他的就交給框架去處理,非常方便,...
Scrapy 框架入門簡介
摘要:解析的方法,每個初始完成下載后將被調用,調用的時候傳入從每一個傳回的對象來作為唯一參數,主要作用如下負責解析返回的網頁數據,提取結構化數據生成生成需要下一頁的請求。 Scrapy 框架 Scrapy是用純Python實現一個為了爬取網站數據、提取結構性數據而編寫的應用框架,用途非常廣泛。 框架的力量,用戶只需要定制開發幾個模塊就可以輕松的實現一個爬蟲,用來抓取網頁內容以及各種圖片,非常...
發表評論
0條評論
harriszh
男|高級講師
TA的文章
閱讀更多
BUI Webapp用于項目中的一點小心得
閱讀 1682·2019-08-30 15:54
前端面試題總結——綜合問題(持續更新中)
閱讀 3332·2019-08-26 17:15
在瀏覽器調起本地應用的方法
閱讀 3522·2019-08-26 13:49
leetcode 鏈表相關題目解析
閱讀 2582·2019-08-26 13:38
【刷算法】丑數
閱讀 2291·2019-08-26 12:08
webstorm預覽html配置localhost為本機ip地址
閱讀 3035·2019-08-26 10:41
籃球即時比分api接口調用示例代碼
閱讀 1369·2019-08-26 10:24
Webpack包教不包會
閱讀 3376·2019-08-23 18:35
<閱讀需要支付1元查看