欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Python的爬蟲程序編寫框架Scrapy入門學(xué)習(xí)教程

 更新時(shí)間:2016年07月02日 16:00:34   作者:Andrew_liu  
Python的一大優(yōu)勢(shì)就是可以輕松制作Web爬蟲,而超高人氣的Scrapy則是名副其實(shí)的Python編寫爬蟲的利器,這里我們就來看一下Python的爬蟲程序編寫框架Scrapy入門學(xué)習(xí)教程:

1. Scrapy簡(jiǎn)介
Scrapy是一個(gè)為了爬取網(wǎng)站數(shù)據(jù),提取結(jié)構(gòu)性數(shù)據(jù)而編寫的應(yīng)用框架。 可以應(yīng)用在包括數(shù)據(jù)挖掘,信息處理或存儲(chǔ)歷史數(shù)據(jù)等一系列的程序中。
其最初是為了頁面抓取 (更確切來說, 網(wǎng)絡(luò)抓取 )所設(shè)計(jì)的, 也可以應(yīng)用在獲取API所返回的數(shù)據(jù)(例如 Amazon Associates Web Services ) 或者通用的網(wǎng)絡(luò)爬蟲。Scrapy用途廣泛,可以用于數(shù)據(jù)挖掘、監(jiān)測(cè)和自動(dòng)化測(cè)試
Scrapy 使用了 Twisted異步網(wǎng)絡(luò)庫(kù)來處理網(wǎng)絡(luò)通訊。整體架構(gòu)大致如下

201672163134410.png (550×388)

Scrapy主要包括了以下組件:

(1)引擎(Scrapy): 用來處理整個(gè)系統(tǒng)的數(shù)據(jù)流處理, 觸發(fā)事務(wù)(框架核心)

(2)調(diào)度器(Scheduler): 用來接受引擎發(fā)過來的請(qǐng)求, 壓入隊(duì)列中, 并在引擎再次請(qǐng)求的時(shí)候返回. 可以想像成一個(gè)URL(抓取網(wǎng)頁的網(wǎng)址或者說是鏈接)的優(yōu)先隊(duì)列, 由它來決定下一個(gè)要抓取的網(wǎng)址是什么, 同時(shí)去除重復(fù)的網(wǎng)址

(3)下載器(Downloader): 用于下載網(wǎng)頁內(nèi)容, 并將網(wǎng)頁內(nèi)容返回給蜘蛛(Scrapy下載器是建立在twisted這個(gè)高效的異步模型上的)

(4)爬蟲(Spiders): 爬蟲是主要干活的, 用于從特定的網(wǎng)頁中提取自己需要的信息, 即所謂的實(shí)體(Item)。用戶也可以從中提取出鏈接,讓Scrapy繼續(xù)抓取下一個(gè)頁面

項(xiàng)目管道(Pipeline): 負(fù)責(zé)處理爬蟲從網(wǎng)頁中抽取的實(shí)體,主要的功能是持久化實(shí)體、驗(yàn)證實(shí)體的有效性、清除不需要的信息。當(dāng)頁面被爬蟲解析后,將被發(fā)送到項(xiàng)目管道,并經(jīng)過幾個(gè)特定的次序處理數(shù)據(jù)。

(5)下載器中間件(Downloader Middlewares): 位于Scrapy引擎和下載器之間的框架,主要是處理Scrapy引擎與下載器之間的請(qǐng)求及響應(yīng)。

(6)爬蟲中間件(Spider Middlewares): 介于Scrapy引擎和爬蟲之間的框架,主要工作是處理蜘蛛的響應(yīng)輸入和請(qǐng)求輸出。

(7)調(diào)度中間件(Scheduler Middewares): 介于Scrapy引擎和調(diào)度之間的中間件,從Scrapy引擎發(fā)送到調(diào)度的請(qǐng)求和響應(yīng)。

Scrapy運(yùn)行流程大概如下:

首先,引擎從調(diào)度器中取出一個(gè)鏈接(URL)用于接下來的抓取
引擎把URL封裝成一個(gè)請(qǐng)求(Request)傳給下載器,下載器把資源下載下來,并封裝成應(yīng)答包(Response)
然后,爬蟲解析Response
若是解析出實(shí)體(Item),則交給實(shí)體管道進(jìn)行進(jìn)一步的處理。
若是解析出的是鏈接(URL),則把URL交給Scheduler等待抓取

2. 安裝Scrapy
使用以下命令:

sudo pip install virtualenv #安裝虛擬環(huán)境工具
virtualenv ENV #創(chuàng)建一個(gè)虛擬環(huán)境目錄
source ./ENV/bin/active #激活虛擬環(huán)境
pip install Scrapy
#驗(yàn)證是否安裝成功
pip list
#輸出如下
cffi (0.8.6)
cryptography (0.6.1)
cssselect (0.9.1)
lxml (3.4.1)
pip (1.5.6)
pycparser (2.10)
pyOpenSSL (0.14)
queuelib (1.2.2)
Scrapy (0.24.4)
setuptools (3.6)
six (1.8.0)
Twisted (14.0.2)
w3lib (1.10.0)
wsgiref (0.1.2)
zope.interface (4.1.1)

更多虛擬環(huán)境的操作可以查看我的博文

3. Scrapy Tutorial
在抓取之前, 你需要新建一個(gè)Scrapy工程. 進(jìn)入一個(gè)你想用來保存代碼的目錄,然后執(zhí)行:

$ scrapy startproject tutorial

這個(gè)命令會(huì)在當(dāng)前目錄下創(chuàng)建一個(gè)新目錄 tutorial, 它的結(jié)構(gòu)如下:

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

這些文件主要是:

(1)scrapy.cfg: 項(xiàng)目配置文件
(2)tutorial/: 項(xiàng)目python模塊, 之后您將在此加入代碼
(3)tutorial/items.py: 項(xiàng)目items文件
(4)tutorial/pipelines.py: 項(xiàng)目管道文件
(5)tutorial/settings.py: 項(xiàng)目配置文件
(6)tutorial/spiders: 放置spider的目錄

3.1. 定義Item
Items是將要裝載抓取的數(shù)據(jù)的容器,它工作方式像 python 里面的字典,但它提供更多的保護(hù),比如對(duì)未定義的字段填充以防止拼寫錯(cuò)誤

通過創(chuàng)建scrapy.Item類, 并且定義類型為 scrapy.Field 的類屬性來聲明一個(gè)Item.
我們通過將需要的item模型化,來控制從 dmoz.org 獲得的站點(diǎn)數(shù)據(jù),比如我們要獲得站點(diǎn)的名字,url 和網(wǎng)站描述,我們定義這三種屬性的域。在 tutorial 目錄下的 items.py 文件編輯

from scrapy.item import Item, Field


class DmozItem(Item):
 # define the fields for your item here like:
 name = Field()
 description = Field()
 url = Field()

3.2. 編寫Spider
Spider 是用戶編寫的類, 用于從一個(gè)域(或域組)中抓取信息, 定義了用于下載的URL的初步列表, 如何跟蹤鏈接,以及如何來解析這些網(wǎng)頁的內(nèi)容用于提取items。

要建立一個(gè) Spider,繼承 scrapy.Spider 基類,并確定三個(gè)主要的、強(qiáng)制的屬性:

name:爬蟲的識(shí)別名,它必須是唯一的,在不同的爬蟲中你必須定義不同的名字.
start_urls:包含了Spider在啟動(dòng)時(shí)進(jìn)行爬取的url列表。因此,第一個(gè)被獲取到的頁面將是其中之一。后續(xù)的URL則從初始的URL獲取到的數(shù)據(jù)中提取。我們可以利用正則表達(dá)式定義和過濾需要進(jìn)行跟進(jìn)的鏈接。
parse():是spider的一個(gè)方法。被調(diào)用時(shí),每個(gè)初始URL完成下載后生成的 Response 對(duì)象將會(huì)作為唯一的參數(shù)傳遞給該函數(shù)。該方法負(fù)責(zé)解析返回的數(shù)據(jù)(response data),提取數(shù)據(jù)(生成item)以及生成需要進(jìn)一步處理的URL的 Request 對(duì)象。
這個(gè)方法負(fù)責(zé)解析返回的數(shù)據(jù)、匹配抓取的數(shù)據(jù)(解析為 item )并跟蹤更多的 URL。
在 /tutorial/tutorial/spiders 目錄下創(chuàng)建 dmoz_spider.py

import scrapy

class DmozSpider(scrapy.Spider):
 name = "dmoz"
 allowed_domains = ["dmoz.org"]
 start_urls = [
  "http://www.dmoz.org/Computers/Programming/Languages/Python/Books/",
  "http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/"
 ]

 def parse(self, response):
  filename = response.url.split("/")[-2]
  with open(filename, 'wb') as f:
   f.write(response.body)

3.3. 爬取
當(dāng)前項(xiàng)目結(jié)構(gòu)

├── scrapy.cfg
└── tutorial
 ├── __init__.py
 ├── items.py
 ├── pipelines.py
 ├── settings.py
 └── spiders
  ├── __init__.py
  └── dmoz_spider.py

到項(xiàng)目根目錄, 然后運(yùn)行命令:

$ scrapy crawl dmoz

運(yùn)行結(jié)果:
2014-12-15 09:30:59+0800 [scrapy] INFO: Scrapy 0.24.4 started (bot: tutorial)
2014-12-15 09:30:59+0800 [scrapy] INFO: Optional features available: ssl, http11
2014-12-15 09:30:59+0800 [scrapy] INFO: Overridden settings: {'NEWSPIDER_MODULE': 'tutorial.spiders', 'SPIDER_MODULES': ['tutorial.spiders'], 'BOT_NAME': 'tutorial'}
2014-12-15 09:30:59+0800 [scrapy] INFO: Enabled extensions: LogStats, TelnetConsole, CloseSpider, WebService, CoreStats, SpiderState
2014-12-15 09:30:59+0800 [scrapy] INFO: Enabled downloader middlewares: HttpAuthMiddleware, DownloadTimeoutMiddleware, UserAgentMiddleware, RetryMiddleware, DefaultHeadersMiddleware, MetaRefreshMiddleware, HttpCompressionMiddleware, RedirectMiddleware, CookiesMiddleware, ChunkedTransferMiddleware, DownloaderStats
2014-12-15 09:30:59+0800 [scrapy] INFO: Enabled spider middlewares: HttpErrorMiddleware, OffsiteMiddleware, RefererMiddleware, UrlLengthMiddleware, DepthMiddleware
2014-12-15 09:30:59+0800 [scrapy] INFO: Enabled item pipelines:
2014-12-15 09:30:59+0800 [dmoz] INFO: Spider opened
2014-12-15 09:30:59+0800 [dmoz] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2014-12-15 09:30:59+0800 [scrapy] DEBUG: Telnet console listening on 127.0.0.1:6023
2014-12-15 09:30:59+0800 [scrapy] DEBUG: Web service listening on 127.0.0.1:6080
2014-12-15 09:31:00+0800 [dmoz] DEBUG: Crawled (200) <GET http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/> (referer: None)
2014-12-15 09:31:00+0800 [dmoz] DEBUG: Crawled (200) <GET http://www.dmoz.org/Computers/Programming/Languages/Python/Books/> (referer: None)
2014-12-15 09:31:00+0800 [dmoz] INFO: Closing spider (finished)
2014-12-15 09:31:00+0800 [dmoz] INFO: Dumping Scrapy stats:
 {'downloader/request_bytes': 516,
  'downloader/request_count': 2,
  'downloader/request_method_count/GET': 2,
  'downloader/response_bytes': 16338,
  'downloader/response_count': 2,
  'downloader/response_status_count/200': 2,
  'finish_reason': 'finished',
  'finish_time': datetime.datetime(2014, 12, 15, 1, 31, 0, 666214),
  'log_count/DEBUG': 4,
  'log_count/INFO': 7,
  'response_received_count': 2,
  'scheduler/dequeued': 2,
  'scheduler/dequeued/memory': 2,
  'scheduler/enqueued': 2,
  'scheduler/enqueued/memory': 2,
  'start_time': datetime.datetime(2014, 12, 15, 1, 30, 59, 533207)}
2014-12-15 09:31:00+0800 [dmoz] INFO: Spider closed (finished)

3.4. 提取Items
3.4.1. 介紹Selector
從網(wǎng)頁中提取數(shù)據(jù)有很多方法。Scrapy使用了一種基于 XPath 或者 CSS 表達(dá)式機(jī)制: Scrapy Selectors

出XPath表達(dá)式的例子及對(duì)應(yīng)的含義:

  • /html/head/title: 選擇HTML文檔中 <head> 標(biāo)簽內(nèi)的 <title> 元素
  • /html/head/title/text(): 選擇 <title> 元素內(nèi)的文本
  • //td: 選擇所有的 <td> 元素
  • //div[@class="mine"]: 選擇所有具有class="mine" 屬性的 div 元素

等多強(qiáng)大的功能使用可以查看XPath tutorial

為了方便使用 XPaths,Scrapy 提供 Selector 類, 有四種方法 :

  • xpath():返回selectors列表, 每一個(gè)selector表示一個(gè)xpath參數(shù)表達(dá)式選擇的節(jié)點(diǎn).
  • css() : 返回selectors列表, 每一個(gè)selector表示CSS參數(shù)表達(dá)式選擇的節(jié)點(diǎn)
  • extract():返回一個(gè)unicode字符串,該字符串為XPath選擇器返回的數(shù)據(jù)
  • re(): 返回unicode字符串列表,字符串作為參數(shù)由正則表達(dá)式提取出來

3.4.2. 取出數(shù)據(jù)

  • 首先使用谷歌瀏覽器開發(fā)者工具, 查看網(wǎng)站源碼, 來看自己需要取出的數(shù)據(jù)形式(這種方法比較麻煩), 更簡(jiǎn)單的方法是直接對(duì)感興趣的東西右鍵審查元素, 可以直接查看網(wǎng)站源碼

在查看網(wǎng)站源碼后, 網(wǎng)站信息在第二個(gè)<ul>內(nèi)

<ul class="directory-url" style="margin-left:0;">

 <li><a  class="listinglink">Core Python Programming</a> 
- By Wesley J. Chun; Prentice Hall PTR, 2001, ISBN 0130260363. For experienced developers to improve extant skills; professional level examples. Starts by introducing syntax, objects, error handling, functions, classes, built-ins. [Prentice Hall]
<div class="flag"><a href="/public/flag?cat=Computers%2FProgramming%2FLanguages%2FPython%2FBooks&url=http%3A%2F%2Fwww.pearsonhighered.com%2Feducator%2Facademic%2Fproduct%2F0%2C%2C0130260363%2C00%252Ben-USS_01DBC.html"><img src="/img/flag.png" alt="[!]" title="report an issue with this listing"></a></div>
</li>
...省略部分...
</ul>

那么就可以通過一下方式進(jìn)行提取數(shù)據(jù)

#通過如下命令選擇每個(gè)在網(wǎng)站中的 <li> 元素:
sel.xpath('//ul/li')

#網(wǎng)站描述:
sel.xpath('//ul/li/text()').extract()

#網(wǎng)站標(biāo)題:
sel.xpath('//ul/li/a/text()').extract()

#網(wǎng)站鏈接:
sel.xpath('//ul/li/a/@href').extract()

如前所述,每個(gè) xpath() 調(diào)用返回一個(gè) selectors 列表,所以我們可以結(jié)合 xpath() 去挖掘更深的節(jié)點(diǎn)。我們將會(huì)用到這些特性,所以:

for sel in response.xpath('//ul/li')
 title = sel.xpath('a/text()').extract()
 link = sel.xpath('a/@href').extract()
 desc = sel.xpath('text()').extract()
 print title, link, desc

在已有的爬蟲文件中修改代碼

import scrapy

class DmozSpider(scrapy.Spider):
 name = "dmoz"
 allowed_domains = ["dmoz.org"]
 start_urls = [
  "http://www.dmoz.org/Computers/Programming/Languages/Python/Books/",
  "http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/"
 ]

 def parse(self, response):
  for sel in response.xpath('//ul/li'):
   title = sel.xpath('a/text()').extract()
   link = sel.xpath('a/@href').extract()
   desc = sel.xpath('text()').extract()
   print title, link, desc

3.4.3. 使用item
Item對(duì)象是自定義的python字典,可以使用標(biāo)準(zhǔn)的字典語法來獲取到其每個(gè)字段的值(字段即是我們之前用Field賦值的屬性)

>>> item = DmozItem()
>>> item['title'] = 'Example title'
>>> item['title']
'Example title'

一般來說,Spider將會(huì)將爬取到的數(shù)據(jù)以 Item 對(duì)象返回, 最后修改爬蟲類,使用 Item 來保存數(shù)據(jù),代碼如下

from scrapy.spider import Spider
from scrapy.selector import Selector
from tutorial.items import DmozItem


class DmozSpider(Spider):
 name = "dmoz"
 allowed_domains = ["dmoz.org"]
 start_urls = [
  "http://www.dmoz.org/Computers/Programming/Languages/Python/Books/",
  "http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/",
 ]

 def parse(self, response):
  sel = Selector(response)
  sites = sel.xpath('//ul[@class="directory-url"]/li')
  items = []

  for site in sites:
   item = DmozItem()
   item['name'] = site.xpath('a/text()').extract()
   item['url'] = site.xpath('a/@href').extract()
   item['description'] = site.xpath('text()').re('-\s[^\n]*\\r')
   items.append(item)
  return items

3.5. 使用Item Pipeline
當(dāng)Item在Spider中被收集之后,它將會(huì)被傳遞到Item Pipeline,一些組件會(huì)按照一定的順序執(zhí)行對(duì)Item的處理。
每個(gè)item pipeline組件(有時(shí)稱之為ItemPipeline)是實(shí)現(xiàn)了簡(jiǎn)單方法的Python類。他們接收到Item并通過它執(zhí)行一些行為,同時(shí)也決定此Item是否繼續(xù)通過pipeline,或是被丟棄而不再進(jìn)行處理。
以下是item pipeline的一些典型應(yīng)用:

  • 清理HTML數(shù)據(jù)
  • 驗(yàn)證爬取的數(shù)據(jù)(檢查item包含某些字段)
  • 查重(并丟棄)
  • 將爬取結(jié)果保存,如保存到數(shù)據(jù)庫(kù)、XML、JSON等文件中

編寫你自己的item pipeline很簡(jiǎn)單,每個(gè)item pipeline組件是一個(gè)獨(dú)立的Python類,同時(shí)必須實(shí)現(xiàn)以下方法:

(1)process_item(item, spider)  #每個(gè)item pipeline組件都需要調(diào)用該方法,這個(gè)方法必須返回一個(gè) Item (或任何繼承類)對(duì)象,或是拋出 DropItem異常,被丟棄的item將不會(huì)被之后的pipeline組件所處理。

#參數(shù):

item: 由 parse 方法返回的 Item 對(duì)象(Item對(duì)象)

spider: 抓取到這個(gè) Item 對(duì)象對(duì)應(yīng)的爬蟲對(duì)象(Spider對(duì)象)

(2)open_spider(spider)  #當(dāng)spider被開啟時(shí),這個(gè)方法被調(diào)用。

#參數(shù):

spider : (Spider object) – 被開啟的spider

(3)close_spider(spider)  #當(dāng)spider被關(guān)閉時(shí),這個(gè)方法被調(diào)用,可以再爬蟲關(guān)閉后進(jìn)行相應(yīng)的數(shù)據(jù)處理。

#參數(shù):

spider : (Spider object) – 被關(guān)閉的spider

為JSON文件編寫一個(gè)items

from scrapy.exceptions import DropItem

class TutorialPipeline(object):

 # put all words in lowercase
 words_to_filter = ['politics', 'religion']

 def process_item(self, item, spider):
  for word in self.words_to_filter:
   if word in unicode(item['description']).lower():
    raise DropItem("Contains forbidden word: %s" % word)
  else:
   return item

在 settings.py 中設(shè)置ITEM_PIPELINES激活item pipeline,其默認(rèn)為[]

ITEM_PIPELINES = {'tutorial.pipelines.FilterWordsPipeline': 1}

3.6. 存儲(chǔ)數(shù)據(jù)
使用下面的命令存儲(chǔ)為json文件格式

scrapy crawl dmoz -o items.json

4.示例
4.1最簡(jiǎn)單的spider(默認(rèn)的Spider)
用實(shí)例屬性start_urls中的URL構(gòu)造Request對(duì)象
框架負(fù)責(zé)執(zhí)行request
將request返回的response對(duì)象傳遞給parse方法做分析

簡(jiǎn)化后的源碼:

class Spider(object_ref):
 """Base class for scrapy spiders. All spiders must inherit from this
 class.
 """
 
 name = None
 
 def __init__(self, name=None, **kwargs):
  if name is not None:
   self.name = name
  elif not getattr(self, 'name', None):
   raise ValueError("%s must have a name" % type(self).__name__)
  self.__dict__.update(kwargs)
  if not hasattr(self, 'start_urls'):
   self.start_urls = []
 
 def start_requests(self):
  for url in self.start_urls:
   yield self.make_requests_from_url(url)
 
 def make_requests_from_url(self, url):
  return Request(url, dont_filter=True)
 
 def parse(self, response):
  raise NotImplementedError
 
 
BaseSpider = create_deprecated_class('BaseSpider', Spider)

一個(gè)回調(diào)函數(shù)返回多個(gè)request的例子

import scrapyfrom myproject.items import MyItemclass MySpider(scrapy.Spider):
 name = 'example.com'
 allowed_domains = ['example.com']
 start_urls = [
  'http://www.example.com/1.html',
  'http://www.example.com/2.html',
  'http://www.example.com/3.html',
 ]
 
 def parse(self, response):
  sel = scrapy.Selector(response)
  for h3 in response.xpath('//h3').extract():
   yield MyItem(title=h3)
 
  for url in response.xpath('//a/@href').extract():
   yield scrapy.Request(url, callback=self.parse)

構(gòu)造一個(gè)Request對(duì)象只需兩個(gè)參數(shù): URL和回調(diào)函數(shù)

4.2CrawlSpider
通常我們需要在spider中決定:哪些網(wǎng)頁上的鏈接需要跟進(jìn), 哪些網(wǎng)頁到此為止,無需跟進(jìn)里面的鏈接。CrawlSpider為我們提供了有用的抽象——Rule,使這類爬取任務(wù)變得簡(jiǎn)單。你只需在rule中告訴scrapy,哪些是需要跟進(jìn)的。
回憶一下我們爬行mininova網(wǎng)站的spider.

class MininovaSpider(CrawlSpider):
 name = 'mininova'
 allowed_domains = ['mininova.org']
 start_urls = ['http://www.mininova.org/yesterday']
 rules = [Rule(LinkExtractor(allow=['/tor/\d+']), 'parse_torrent')]
 
 def parse_torrent(self, response):
  torrent = TorrentItem()
  torrent['url'] = response.url
  torrent['name'] = response.xpath("http://h1/text()").extract()
  torrent['description'] = response.xpath("http://div[@id='description']").extract()
  torrent['size'] = response.xpath("http://div[@id='specifications']/p[2]/text()[2]").extract()
  return torrent

上面代碼中 rules的含義是:匹配/tor/\d+的URL返回的內(nèi)容,交給parse_torrent處理,并且不再跟進(jìn)response上的URL。
官方文檔中也有個(gè)例子:

 rules = (
  # 提取匹配 'category.php' (但不匹配 'subsection.php') 的鏈接并跟進(jìn)鏈接(沒有callback意味著follow默認(rèn)為True)
  Rule(LinkExtractor(allow=('category\.php', ), deny=('subsection\.php', ))),
 
  # 提取匹配 'item.php' 的鏈接并使用spider的parse_item方法進(jìn)行分析
  Rule(LinkExtractor(allow=('item\.php', )), callback='parse_item'),
 )

除了Spider和CrawlSpider外,還有XMLFeedSpider, CSVFeedSpider, SitemapSpider

相關(guān)文章

最新評(píng)論