成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

scrapy學(xué)習(xí)筆記

luzhuqun / 535人閱讀

摘要:是最有名的爬蟲框架之一,可以很方便的進(jìn)行抓取,并且提供了很強的定制型,這里記錄簡單學(xué)習(xí)的過程和在實際應(yīng)用中會遇到的一些常見問題一安裝在安裝之前有一些依賴需要安裝,否則可能會安裝失敗,的選擇器依賴于,還有網(wǎng)絡(luò)引擎,下面是下安裝的過程下安裝安裝

scrapy是python最有名的爬蟲框架之一,可以很方便的進(jìn)行web抓取,并且提供了很強的定制型,這里記錄簡單學(xué)習(xí)的過程和在實際應(yīng)用中會遇到的一些常見問題

一、安裝

在安裝scrapy之前有一些依賴需要安裝,否則可能會安裝失敗,scrapy的選擇器依賴于lxml,還有Twisted網(wǎng)絡(luò)引擎,下面是ubuntu下安裝的過程

1. linux下安裝
# 1. 安裝xml依賴庫
$ sudo apt-get install libxml2 libxml2-dev
$ sudo apt-get install libxslt1-dev
$ sudo apt-get install python-libxml2

# 2. 安裝lxml
$ sudo pip install lxml

# 3. 安裝Twisted(版本可以換成最新的),用pip也可以,如果失敗的話下載源碼安裝,如下
$ wget https://pypi.python.org/packages/6b/23/8dbe86fc83215015e221fbd861a545c6ec5c9e9cd7514af114d1f64084ab/Twisted-16.4.1.tar.bz2#md5=c6d09bdd681f538369659111f079c29d
$ tar xjf Twisted-16.4.1.tar.bz2
$ cd Twisted-16.4.1
$ sudo python setup.py install

# 3. 安裝scrapy
$ sudo pip install scrapy

http://lxml.de/installation.html

2. Mac下安裝
# 安裝xml依賴庫
$ xcode-select —install

# 其實相關(guān)依賴pip會自動幫我們裝上
$ pip install scrapy

mac下安裝有時候會失敗,建議使用virtualenv安裝在獨立的環(huán)境下,可以減少一些問題,因為mac系統(tǒng)自帶python,例如一些依賴庫依賴的一些新的版本,而升級新版本會把舊版本卸載掉,卸載可能會有權(quán)限的問題

二、基本使用 1. 初始化scrapy項目

我們可以使用命令行初始化一個項目

$ scrapy startproject tutorial

這里可以查看scrapy更多其他的命令

初始化完成后,我們得到下面目錄結(jié)構(gòu)

scrapy.cfg:         項目的配置文件
tutorial/:          該項目的python模塊, 在這里添加代碼
    items.py:       項目中的item文件
    pipelines.py:   項目中的pipelines文件.
    settings.py:    項目全局設(shè)置文件.
    spiders/        爬蟲模塊目錄

我們先看一下scrapy的處理流程

scrapy由下面幾個部分組成

spiders:爬蟲模塊,負(fù)責(zé)配置需要爬取的數(shù)據(jù)和爬取規(guī)則,以及解析結(jié)構(gòu)化數(shù)據(jù)

items:定義我們需要的結(jié)構(gòu)化數(shù)據(jù),使用相當(dāng)于dict

pipelines:管道模塊,處理spider模塊分析好的結(jié)構(gòu)化數(shù)據(jù),如保存入庫等

middlewares:中間件,相當(dāng)于鉤子,可以對爬取前后做預(yù)處理,如修改請求header,url過濾等

我們先來看一個例子,在spiders目錄下新建一個模塊DmozSpider.py

import scrapy

class DmozSpider(scrapy.Spider):
    # 必須定義
    name = "dmoz"
    # 初始urls
    start_urls = [
        "http://www.dmoz.org/Computers/Programming/Languages/Python/Books/",
        "http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/"
    ]

    # 默認(rèn)response處理函數(shù)
    def parse(self, response):
        # 把結(jié)果寫到文件中
        filename = response.url.split("/")[-2]
        with open(filename, "wb") as f:
            f.write(response.body)

打開終端進(jìn)入根目錄,執(zhí)行下面命令

$ scrapy crawl dmoz

爬蟲開始爬取start_urls定義的url,并輸出到文件中,最后輸出爬去報告,會輸出爬取得統(tǒng)計結(jié)果

2016-09-13 10:36:43 [scrapy] INFO: Spider opened
2016-09-13 10:36:43 [scrapy] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2016-09-13 10:36:43 [scrapy] DEBUG: Telnet console listening on 127.0.0.1:6023
2016-09-13 10:36:44 [scrapy] DEBUG: Crawled (200)  (referer: None)
2016-09-13 10:36:45 [scrapy] DEBUG: Crawled (200)  (referer: None)
2016-09-13 10:36:45 [scrapy] INFO: Closing spider (finished)
2016-09-13 10:36:45 [scrapy] INFO: Dumping Scrapy stats:
{"downloader/request_bytes": 548,
 "downloader/request_count": 2,
 "downloader/request_method_count/GET": 2,
 "downloader/response_bytes": 16179,
 "downloader/response_count": 2,
 "downloader/response_status_count/200": 2,
 "finish_reason": "finished",
 "finish_time": datetime.datetime(2016, 9, 13, 2, 36, 45, 585113),
 "log_count/DEBUG": 3,
 "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(2016, 9, 13, 2, 36, 43, 935790)}
2016-09-13 10:36:45 [scrapy] INFO: Spider closed (finished)

這里我們完成了簡單的爬取和保存的操作,會在根目錄生成兩個文件ResourcesBooks

2. 通過代碼運行爬蟲

每次進(jìn)入控制臺運行爬蟲還是比較麻煩的,而且不好調(diào)試,我們可以通過CrawlerProcess通過代碼運行爬蟲,新建一個模塊run.py

from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settings

from spiders.DmozSpider import DmozSpider

# 獲取settings.py模塊的設(shè)置
settings = get_project_settings()
process = CrawlerProcess(settings=settings)

# 可以添加多個spider
# process.crawl(Spider1)
# process.crawl(Spider2)
process.crawl(DmozSpider)

# 啟動爬蟲,會阻塞,直到爬取完成
process.start()

參考:http://doc.scrapy.org/en/latest/topics/practices.html#run-scrapy-from-a-script

三、Scrapy類

如上面的DmozSpider類,爬蟲類繼承自scrapy.Spider,用于構(gòu)造Request對象給Scheduler

1. 常用屬性與方法

屬性

name:爬蟲的名字,必須唯一(如果在控制臺使用的話,必須配置)

start_urls:爬蟲初始爬取的鏈接列表

parse:response結(jié)果處理函數(shù)

custom_settings:自定義配置,覆蓋settings.py中的默認(rèn)配置

方法

start_requests:啟動爬蟲的時候調(diào)用,默認(rèn)是調(diào)用make_requests_from_url方法爬取start_urls的鏈接,可以在這個方法里面定制,如果重寫了該方法,start_urls默認(rèn)將不會被使用,可以在這個方法里面定制一些自定義的url,如登錄,從數(shù)據(jù)庫讀取url等,本方法返回Request對象

make_requests_from_url:默認(rèn)由start_requests調(diào)用,可以配置Request對象,返回Request對象

parse:response到達(dá)spider的時候默認(rèn)調(diào)用,如果在Request對象配置了callback函數(shù),則不會調(diào)用,parse方法可以迭代返回ItemRequest對象,如果返回Request對象,則會進(jìn)行增量爬取

2. Request與Response對象

每個請求都是一個Request對象,Request對象定義了請求的相關(guān)信息(url, method, headers, body, cookie, priority)和回調(diào)的相關(guān)信息(meta, callback, dont_filter, errback),通常由spider迭代返回

其中meta相當(dāng)于附加變量,可以在請求完成后通過response.meta訪問

請求完成后,會通過Response對象發(fā)送給spider處理,常用屬性有(url, status, headers, body, request, meta, )

詳細(xì)介紹參考官網(wǎng)

https://doc.scrapy.org/en/latest/topics/request-response.html#request-objects

https://doc.scrapy.org/en/latest/topics/request-response.html#response-objects

看下面這個例子

from scrapy import Spider
from scrapy import Request

class TestSpider(Spider):
    name = "test"
    start_urls = [
        "http://www.qq.com/",
    ]

    def login_parse(self, response):
        """ 如果登錄成功,手動構(gòu)造請求Request迭代返回 """
        print response
        for i in range(0, 10):
            yield Request("http://www.example.com/list/1?page={0}".format(i))

    def start_requests(self):
        """ 覆蓋默認(rèn)的方法(忽略start_urls),返回登錄請求頁,制定處理函數(shù)為login_parse """
        return Request("http://www.example.com/login", method="POST" body="username=bomo&pwd=123456", callback=self.login_parse)


    def parse(self, response):
        """ 默認(rèn)請求處理函數(shù) """
        print response
四、Selector

上面我們只是爬取了網(wǎng)頁的html文本,對于爬蟲,我們需要明確我們需要爬取的結(jié)構(gòu)化數(shù)據(jù),需要對原文本進(jìn)行解析,解析的方法通常有下面這些

普通文本操作

正則表達(dá)式:re

Dom樹操作:BeautifulSoup

XPath選擇器:lxml

scrapy默認(rèn)支持選擇器的功能,自帶的選擇器構(gòu)建與lxml之上,并對其進(jìn)行了改進(jìn),使用起來更為簡潔明了

1. XPath選擇器

XPpath是標(biāo)準(zhǔn)的XML文檔查詢語言,可以用于查詢XML文檔中的節(jié)點和內(nèi)容,關(guān)于XPath語法,可以參見這里

先看一個例子,通過html或xml構(gòu)造Selector對象,然后通過xpath查詢節(jié)點,并解析出節(jié)點的內(nèi)容

from scrapy import Selector

html = "goodbuy"
sel = Selector(text=html)
nodes = sel.xpath("http://span")
for node in nodes:
    print node.extract()

Selector相當(dāng)于節(jié)點,通過xpath去到子節(jié)點集合(SelectorList),可以繼續(xù)搜索,通過extract方法可以取出節(jié)點的值,extract方法也可以作用于SelectorList,對于SelectorList可以通過extract_first取出第一個節(jié)點的值

通過text()取出節(jié)點的內(nèi)容

通過@href去除節(jié)點屬性值(這里是取出href屬性的值)

直接對節(jié)點取值,則是輸出節(jié)點的字符串

2. CSS選擇器

除了XPath選擇器,scrapy還支持css選擇器

html = """
        
            
                good
                buy
                
            
        
        """
sel = Selector(text=html)

# 選擇class為video_part_lists的li節(jié)點
lis = sel.css("li.video_part_lists")

for li in lis:
    # 選擇a節(jié)點的屬性
    print li.css("a::attr(href)").extract()

關(guān)于css選擇器更多的規(guī)則,可以見w3c官網(wǎng)

https://www.w3.org/TR/selectors/

五、Item類

上面我們只是爬取了網(wǎng)頁的html文本,對于爬蟲,我們需要明確我們需要爬取的結(jié)構(gòu)化數(shù)據(jù),我們定義一個item存儲分類信息,scrapy的item繼承自scrapy.Item

from scrapy import Item, Field

class DmozItem(Item):
    title = Field()
    link = Field()
    desc = Field()

scrapy.Item的用法與python中的字典用法基本一樣,只是做了一些安全限制,屬性定義使用Field,這里只是進(jìn)行了聲明,而不是真正的屬性,使用的時候通過鍵值對操作,不支持屬性訪問

what, 好坑爹,這意味著所有的屬性賦值都得用字符串了,這里有解釋(還是沒太明白)

why-is-scrapys-field-a-dict

修改DmozSpider的parse方法

class DmozSpider(scrapy.Spider):
    ...
    def parse(self, response):
        for sel in response.xpath("http://ul/li"):
            dmoz_item = DmozItem()
            dmoz_item["title"] = sel.xpath("a/text()").extract()
            dmoz_item["link"] = sel.xpath("a/@href").extract()
            dmoz_item["desc"] = sel.xpath("text()").extract()
            print dmoz_item
六、Pipeline

spider負(fù)責(zé)爬蟲的配置,item負(fù)責(zé)聲明結(jié)構(gòu)化數(shù)據(jù),而對于數(shù)據(jù)的處理,在scrapy中使用管道的方式進(jìn)行處理,只要注冊過的管道都可以處理item數(shù)據(jù)(處理,過濾,保存)

下面看看管道的聲明方式,這里定義一個預(yù)處理管道PretreatmentPipeline.py,如果item的title為None,則設(shè)置為空字符串

class PretreatmentPipeline(object):
    def process_item(self, item, spider):
        if item["title"]:
            # 不讓title為空
            item["title"] = ""
        return item

再定義一個過濾重復(fù)數(shù)據(jù)的管道DuplicatesPipeline.py,當(dāng)link重復(fù),則丟棄

from scrapy.exceptions import DropItem

class DuplicatesPipeline(object):
    def __init__(self):
        self.links = set()

    def process_item(self, item, spider):
        if item["link"] in self.links:
            # 跑出DropItem表示丟掉數(shù)據(jù)
            raise DropItem("Duplicate item found: %s" % item)
        else:
            self.links.add(item["link"])
            return item

最后可以定義一個保存數(shù)據(jù)的管道,可以把數(shù)據(jù)保存到數(shù)據(jù)庫中

from scrapy.exceptions import DropItem
from Database import Database

class DatabasePipeline(object):
    def __init__(self):
        self.db = Database

    def process_item(self, item, spider):
        if self.db.item_exists(item["id"]):
            self.db.update_item(item)
        else:
            self.db.insert_item(item)

定義好管道之后我們需要配置到爬蟲上,我們在settings.py模塊中配置,后面的數(shù)字表示管道的順序

ITEM_PIPELINES = {
    "pipelines.DuplicatesPipeline.DuplicatesPipeline": 1,
    "pipelines.PretreatmentPipeline.PretreatmentPipeline": 2,
}

我們也可以為spider配置多帶帶的pipeline

class TestSpider(Spider):
    # 自定義配置
    custom_settings = {
        # item處理管道
        "ITEM_PIPELINES": {
            "tutorial.pipelines.FangDetailPipeline.FangDetailPipeline": 1,
        },
    }
    ...

除了process_item方法外,pipeline還有open_spiderspider_closed兩個方法,在爬蟲啟動和關(guān)閉的時候調(diào)用

七、Rule

爬蟲的通常需要在一個網(wǎng)頁里面爬去其他的鏈接,然后一層一層往下爬,scrapy提供了LinkExtractor類用于對網(wǎng)頁鏈接的提取,使用LinkExtractor需要使用CrawlSpider爬蟲類中,CrawlSpiderSpider相比主要是多了rules,可以添加一些規(guī)則,先看下面這個例子,爬取鏈家網(wǎng)的鏈接

from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor

class LianjiaSpider(CrawlSpider):
    name = "lianjia"

    allowed_domains = ["lianjia.com"]

    start_urls = [
        "http://bj.lianjia.com/ershoufang/"
    ]

    rules = [
        # 匹配正則表達(dá)式,處理下一頁
        Rule(LinkExtractor(allow=(r"http://bj.lianjia.com/ershoufang/pgs+$",)), callback="parse_item"),

        # 匹配正則表達(dá)式,結(jié)果加到url列表中,設(shè)置請求預(yù)處理函數(shù)
        # Rule(FangLinkExtractor(allow=("http://www.lianjia.com/client/", )), follow=True, process_request="add_cookie")
    ]

    def parse_item(self, response):
        # 這里與之前的parse方法一樣,處理
        pass
1. Rule對象

Role對象有下面參數(shù)

link_extractor:鏈接提取規(guī)則

callback:link_extractor提取的鏈接的請求結(jié)果的回調(diào)

cb_kwargs:附加參數(shù),可以在回調(diào)函數(shù)中獲取到

follow:表示提取的鏈接請求完成后是否還要應(yīng)用當(dāng)前規(guī)則(boolean),如果為False則不會對提取出來的網(wǎng)頁進(jìn)行進(jìn)一步提取,默認(rèn)為False

process_links:處理所有的鏈接的回調(diào),用于處理從response提取的links,通常用于過濾(參數(shù)為link列表)

process_request:鏈接請求預(yù)處理(添加header或cookie等)

2. LinkExtractor

LinkExtractor常用的參數(shù)有:

allow:提取滿足正則表達(dá)式的鏈接

deny:排除正則表達(dá)式匹配的鏈接(優(yōu)先級高于allow

allow_domains:允許的域名(可以是strlist

deny_domains:排除的域名(可以是strlist

restrict_xpaths:提取滿足XPath選擇條件的鏈接(可以是strlist

restrict_css:提取滿足css選擇條件的鏈接(可以是strlist

tags:提取指定標(biāo)簽下的鏈接,默認(rèn)從aarea中提取(可以是strlist

attrs:提取滿足擁有屬性的鏈接,默認(rèn)為href(類型為list

unique:鏈接是否去重(類型為boolean

process_value:值處理函數(shù)(優(yōu)先級大于allow

關(guān)于LinkExtractor的詳細(xì)參數(shù)介紹見官網(wǎng)

注意:如果使用rules規(guī)則,請不要覆蓋或重寫CrawlSpiderparse方法,否則規(guī)則會失效,可以使用parse_start_urls方法

八、Middleware

從最開始的流程圖可以看到,爬去一個資源鏈接的流程,首先我們配置spider相關(guān)的爬取信息,在啟動爬取實例后,scrapy_engine從Spider取出Request(經(jīng)過SpiderMiddleware),然后丟給Scheduler(經(jīng)過SchedulerMiddleware),Scheduler接著把請求丟給Downloader(經(jīng)過DownloadMiddlware),Downloader把請求結(jié)果丟還給Spider,然后Spider把分析好的結(jié)構(gòu)化數(shù)據(jù)丟給Pipeline,Pipeline進(jìn)行分析保存或丟棄,這里面有4個角色

scrapy有下面三種middlewares

SpiderMiddleware:通常用于配置爬蟲相關(guān)的屬性,引用鏈接設(shè)置,Url長度限制,成功狀態(tài)碼設(shè)置,爬取深度設(shè)置,爬去優(yōu)先級設(shè)置等

DownloadMiddlware:通常用于處理下載之前的預(yù)處理,如請求Header(Cookie,User-Agent),登錄驗證處理,重定向處理,代理服務(wù)器處理,超時處理,重試處理等

SchedulerMiddleware(已經(jīng)廢棄):為了簡化框架,調(diào)度器中間件已經(jīng)被廢棄,使用另外兩個中間件已經(jīng)夠用了

1. SpiderMiddleware

爬蟲中間件有下面幾個方法

process_spider_input:當(dāng)response通過spider的時候被調(diào)用,返回None(繼續(xù)給其他中間件處理)或拋出異常(不會給其他中間件處理,當(dāng)成異常處理)

process_spider_output:當(dāng)spider有item或Request輸出的時候調(diào)動

process_spider_exception:處理出現(xiàn)異常時調(diào)用

process_start_requests:spider當(dāng)開始請求Request的時候調(diào)用

下面是scrapy自帶的一些中間件(在scrapy.spidermiddlewares命名空間下)

UrlLengthMiddleware

RefererMiddleware

OffsiteMiddleware

HttpErrorMiddleware

DepthMiddleware

我們自己實現(xiàn)一個SpiderMiddleware

TODO

參考鏈接:http://doc.scrapy.org/en/latest/topics/spider-middleware.html

2. DownloaderMiddleware

下載中間件有下面幾個方法

process_request:請求通過下載器的時候調(diào)用

process_response:請求完成后調(diào)用

process_exception:請求發(fā)生異常時調(diào)用

from_crawler:從crawler構(gòu)造的時候調(diào)用

from_settings:從settings構(gòu)造的時候調(diào)用

``

更多詳細(xì)的參數(shù)解釋見這里

在爬取網(wǎng)頁的時候,使用不同的User-Agent可以提高請求的隨機性,定義一個隨機設(shè)置User-Agent的中間件RandomUserAgentMiddleware

import random

class RandomUserAgentMiddleware(object):
    """Randomly rotate user agents based on a list of predefined ones"""

    def __init__(self, agents):
        self.agents = agents

    # 從crawler構(gòu)造,USER_AGENTS定義在crawler的配置的設(shè)置中
    @classmethod
    def from_crawler(cls, crawler):
        return cls(crawler.settings.getlist("USER_AGENTS"))

    # 從settings構(gòu)造,USER_AGENTS定義在settings.py中
    @classmethod
    def from_settings(cls, settings):
        return cls(settings.getlist("USER_AGENTS"))

    def process_request(self, request, spider):
        # 設(shè)置隨機的User-Agent
        request.headers.setdefault("User-Agent", random.choice(self.agents))

settings.py設(shè)置USER_AGENTS參數(shù)

USER_AGENTS = [
    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
    "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)",
    "Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
    "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)",
    "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)",
    "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)",
    "Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)",
    "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)",
    "Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6",
    "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1",
    "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0",
    "Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5",
    "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Fedora/1.9.0.8-1.fc10 Kazehakase/0.5.6",
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20",
    "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168 Version/11.52",
]

配置爬蟲中間件的方式與pipeline類似,第二個參數(shù)表示優(yōu)先級

# 配置爬蟲中間件
SPIDER_MIDDLEWARES = {
    "myproject.middlewares.CustomSpiderMiddleware": 543,
    # 如果想禁用默認(rèn)的中間件的話,可以設(shè)置其優(yōu)先級為None
    "scrapy.spidermiddlewares.offsite.OffsiteMiddleware": None,
}

# 配置下載中間件
DOWNLOADER_MIDDLEWARES = {
    "myproject.middlewares.RandomUserAgentMiddleware": 543,
    "scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware": None,
}
3. 代理服務(wù)器

爬蟲最怕的就是封ip,這時候就需要代理服務(wù)器來爬取,scrapy設(shè)置代理服務(wù)器非常簡單,只需要在請求前設(shè)置Request對象的meta屬性,添加proxy值即可,通常我們可以通過中間件來做

class ProxyMiddleware(object):
    def process_request(self, request, spider):
        proxy = "https://178.33.6.236:3128"     # 代理服務(wù)器
        request.meta["proxy"] = proxy
九、緩存

scrapy默認(rèn)已經(jīng)自帶了緩存的功能,通常我們只需要配置即可,打開settings.py

# 打開緩存
HTTPCACHE_ENABLED = True

# 設(shè)置緩存過期時間(單位:秒)
#HTTPCACHE_EXPIRATION_SECS = 0

# 緩存路徑(默認(rèn)為:.scrapy/httpcache)
HTTPCACHE_DIR = "httpcache"

# 忽略的狀態(tài)碼
HTTPCACHE_IGNORE_HTTP_CODES = []

# 緩存模式(文件緩存)
HTTPCACHE_STORAGE = "scrapy.extensions.httpcache.FilesystemCacheStorage"

更多參數(shù)參見這里

十、多線程

scrapy網(wǎng)絡(luò)請求是基于Twisted,而Twisted默認(rèn)支持多線程,而且scrapy默認(rèn)也是通過多線程請求的,并且支持多核CPU的并發(fā),通常只需要配置一些參數(shù)即可

# 默認(rèn)Item并發(fā)數(shù):100
CONCURRENT_ITEMS = 100

# 默認(rèn)Request并發(fā)數(shù):16
CONCURRENT_REQUESTS = 16

# 默認(rèn)每個域名的并發(fā)數(shù):8
CONCURRENT_REQUESTS_PER_DOMAIN = 8

# 每個IP的最大并發(fā)數(shù):0表示忽略
CONCURRENT_REQUESTS_PER_IP = 0

更多參數(shù)參見這里

十一、常見問題 1. 項目名稱問題

在使用的時候遇到過一個問題,在初始化scrapy startproject tutorial的時候,如果使用了一些特殊的名字,如:test, fang等單詞的話,通過get_project_settings方法獲取配置的時候會出錯,改成tutorial或一些復(fù)雜的名字的時候不會

ImportError: No module named tutorial.settings

這是一個bug,在github上有提到:https://github.com/scrapy/scrapy/issues/428,但貌似沒有完全修復(fù),修改一下名字就好了(當(dāng)然scrapy.cfgsettings.py里面也需要修改)

2. 為每個pipeline配置spider

上面我們是在settings.py里面配置pipeline,這里的配置的pipeline會作用于所有的spider,我們可以為每一個spider配置不同的pipeline,設(shè)置Spidercustom_settings對象

class LianjiaSpider(CrawlSpider):
    ...
    # 自定義配置
    custom_settings = {
        "ITEM_PIPELINES": {
            "tutorial.pipelines.TestPipeline.TestPipeline": 1,
        }
    }
3. 獲取提取鏈接的節(jié)點信息

通過LinkExtractor提取的scrapy.Link默認(rèn)不帶節(jié)點信息,有時候我們需要節(jié)點的其他attribute屬性,scrapy.Link有個text屬性保存從節(jié)點提取的text值,我們可以通過修改lxmlhtml._collect_string_content變量為etree.tostring,這樣可以在提取節(jié)點值就變味渲染節(jié)點scrapy.Link.text,然后根據(jù)scrapy.Link.text屬性拿到節(jié)點的html,最后提取出我們需要的值

from lxml import etree
import scrapy.linkextractors.lxmlhtml
scrapy.linkextractors.lxmlhtml._collect_string_content = etree.tostring
4. 從數(shù)據(jù)庫中讀取urls

有時候我們已經(jīng)把urls下載到數(shù)據(jù)庫了,而不是在start_urls里配置,這時候可以重載spider的start_requests方法

def start_requests(self):
    for u in self.db.session.query(User.link):
        yield Request(u.link)

我們還可以在Request添加元數(shù)據(jù),然后在response中訪問

def start_requests(self):
    for u in self.db.session.query(User):
        yield Request(u.link, meta={"name": u.name})

def parse(self, response):
    print response.url, response.meta["name"]
5. 如何進(jìn)行循環(huán)爬取

有時候我們需要爬取的一些經(jīng)常更新的頁面,例如:間隔時間為2s,爬去一個列表前10頁的數(shù)據(jù),從第一頁開始爬,爬完成后重新回到第一頁

目前的思路是,通過parse方法迭代返回Request進(jìn)行增量爬取,由于scrapy默認(rèn)由緩存機制,需要修改

6. 關(guān)于去重

scrapy默認(rèn)有自己的去重機制,默認(rèn)使用scrapy.dupefilters.RFPDupeFilter類進(jìn)行去重,主要邏輯如下

if include_headers:
    include_headers = tuple(to_bytes(h.lower())
                             for h in sorted(include_headers))
cache = _fingerprint_cache.setdefault(request, {})
if include_headers not in cache:
    fp = hashlib.sha1()
    fp.update(to_bytes(request.method))
    fp.update(to_bytes(canonicalize_url(request.url)))
    fp.update(request.body or b"")
    if include_headers:
        for hdr in include_headers:
            if hdr in request.headers:
                fp.update(hdr)
                for v in request.headers.getlist(hdr):
                    fp.update(v)
    cache[include_headers] = fp.hexdigest()
return cache[include_headers]

默認(rèn)的去重指紋是sha1(method + url + body + header),這種方式并不能過濾很多,例如有一些請求會加上時間戳的,基本每次都會不同,這時候我們需要自定義過濾規(guī)則

from scrapy.dupefilter import RFPDupeFilter

class CustomURLFilter(RFPDupeFilter):
    """ 只根據(jù)url去重"""

    def __init__(self, path=None):
        self.urls_seen = set()
        RFPDupeFilter.__init__(self, path)

    def request_seen(self, request):
        if request.url in self.urls_seen:
            return True
        else:
            self.urls_seen.add(request.url)

配置setting

DUPEFILTER_CLASS = "tutorial.custom_filters.CustomURLFilter"
7. 如何在Pipeline中處理不同的Item

scrapy所有的迭代出來的的Item都會經(jīng)過所有的Pipeline,如果需要處理不同的Item,只能通過isinstance()方法進(jìn)行類型判斷,然后分別進(jìn)行處理,暫時沒有更好的方案

8. url按順序執(zhí)行

我們可以通過Request的priority控制url的請求的執(zhí)行順序,但由于網(wǎng)絡(luò)請求的不確定性,不能保證返回也是按照順序進(jìn)行的,如果需要進(jìn)行逐個url請求的話,吧url列表放在meta對象里面,在response的時候迭代返回下一個Request對象到調(diào)度器,達(dá)到順序執(zhí)行的目的,暫時沒有更好的方案

十二、總結(jié)

scrapy雖然是最有名的python爬蟲框架,但是還是有很多不足,例如,item不能多帶帶配置給制定的pipeline,每一個爬取的所有item都會走遍所有的管道,需要在管道里面去判斷不同類型的item,如果在pipelines和items比較多的項目,將會讓項目變得非常臃腫

如有問題歡迎到我的博客留言

十三、參考鏈接

官方文檔

中文教程

scrapy五大模塊

最后安利一下自己的博客:http://zhengbomo.github.com

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/38190.html

相關(guān)文章

  • scrapy學(xué)習(xí)筆記(三):使用item與pipeline保存數(shù)據(jù)

    摘要:最近真是忙的吐血。。。上篇寫的是直接在爬蟲中使用,這樣不是很好,下使用才是正經(jīng)方法。 最近真是忙的吐血。。。 上篇寫的是直接在爬蟲中使用mongodb,這樣不是很好,scrapy下使用item才是正經(jīng)方法。在item中定義需要保存的內(nèi)容,然后在pipeline處理item,爬蟲流程就成了這樣: 抓取 --> 按item規(guī)則收集需要數(shù)據(jù) -->使用pipeline處理(存儲等) 定義it...

    13651657101 評論0 收藏0
  • scrapy學(xué)習(xí)筆記(二):連續(xù)抓取與數(shù)據(jù)保存

    摘要:運行一下我們的代碼,然后刷新下數(shù)據(jù)庫,可看到數(shù)據(jù)已經(jīng)保存到中了查看數(shù)據(jù)庫內(nèi)容很清晰,每一項都有保存 抓取論壇、貼吧這種多分頁的信息時,沒接觸scrapy之前,是前確定有多少頁,使用for循環(huán)抓取。這方法略顯笨重,使用scrapy則可以直接組合下一頁的鏈接,然后傳給request持續(xù)進(jìn)行抓取,一直到?jīng)]有下一頁鏈接為止。 還是以官方教程的網(wǎng)站為例子,先分析下元素: showImg(http...

    ShevaKuilin 評論0 收藏0
  • 首次公開,整理12年積累的博客收藏夾,零距離展示《收藏夾吃灰》系列博客

    摘要:時間永遠(yuǎn)都過得那么快,一晃從年注冊,到現(xiàn)在已經(jīng)過去了年那些被我藏在收藏夾吃灰的文章,已經(jīng)太多了,是時候把他們整理一下了。那是因為收藏夾太亂,橡皮擦給設(shè)置私密了,不收拾不好看呀。 ...

    Harriet666 評論0 收藏0
  • 程序員的一些常用技能點

    摘要:其他筆記還有一些其他的主流云筆記軟件,像印象筆記,為知筆記,等,由于沒有多做嘗試,就不加以評價年最好用的云筆記軟件推薦介紹是一種可以使用普通文本編輯器編寫的標(biāo)記語言,通過簡單的標(biāo)記語法,它可以使普通文本內(nèi)容具有一定的格式。 目錄: [TOC] Git Git介紹: Git是目前世界上最先進(jìn)的分布式版本控制系統(tǒng)(沒有之一),簡單來說,Git 是一個管理你的「代碼的歷史記錄」的工具。 Gi...

    baishancloud 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<