上篇文章介紹 Scrapy 框架爬取網(wǎng)站的基本用法秆麸。但是爬蟲程序比較粗糙,很多細(xì)節(jié)還需打磨及汉。本文主要是講解 Scrapy 一些小技巧,能讓爬蟲程序更加完善坷随。
1 設(shè)置 User-agent
Scrapy 官方建議使用 User-Agent 池, 輪流選擇其中一個常用瀏覽器的 User-Agent來作為 User-Agent驻龟。scrapy 發(fā)起的 http 請求中 headers 部分中 User-Agent 字段的默認(rèn)值是Scrapy/VERSION (+http://scrapy.org)
,我們需要修改該字段偽裝成瀏覽器訪問網(wǎng)站缸匪。
- 同樣在 setting.py 中新建存儲 User-Agent 列表,
UserAgent_List = [
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2226.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 6.4; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2225.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2225.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2224.3 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.93 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.93 Safari/537.36",
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 4.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.67 Safari/537.36",
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.67 Safari/537.36",
"Mozilla/5.0 (X11; OpenBSD i386) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.125 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1944.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.3319.102 Safari/537.36",
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.2309.372 Safari/537.36",
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.2117.157 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36",
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1866.237 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.137 Safari/4E423F",
"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1",
"Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.0",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10; rv:33.0) Gecko/20100101 Firefox/33.0",
"Mozilla/5.0 (X11; Linux i586; rv:31.0) Gecko/20100101 Firefox/31.0",
"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:31.0) Gecko/20130401 Firefox/31.0",
"Mozilla/5.0 (Windows NT 5.1; rv:31.0) Gecko/20100101 Firefox/31.0",
"Opera/9.80 (X11; Linux i686; Ubuntu/14.10) Presto/2.12.388 Version/12.16",
"Opera/9.80 (Windows NT 6.0) Presto/2.12.388 Version/12.14",
"Mozilla/5.0 (Windows NT 6.0; rv:2.0) Gecko/20100101 Firefox/4.0 Opera 12.14",
"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0) Opera 12.14",
"Opera/9.80 (Windows NT 5.1; U; zh-sg) Presto/2.9.181 Version/12.00"
]
- 在 middlewares.py 文件中新建一個名為
RandomUserAgentMiddleware
的代理中間層類
import random
from scrapy_demo.settings import UserAgent_List
class RandomUserAgentMiddleware(object):
'''動態(tài)隨機(jī)設(shè)置 User-agent'''
def process_request(self, request, spider):
ua = random.choice(UserAgent_List)
if ua:
request.headers.setdefault('User-Agent', ua)
print(request.headers)
- 在 settings.py 中配置 RandomUserAgentMiddleware , 激活中間件
DOWNLOADER_MIDDLEWARES = {
# 第二行的填寫規(guī)則
# yourproject.middlewares(文件名).middleware類
# 設(shè)置 User-Agent
'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware': None,
'scrapy_demo.middlewares.RandomUserAgentMiddleware': 400, # scrapy_demo 是你項(xiàng)目的名稱
}
2 禁用cookies
有些站點(diǎn)會使用 cookies 來發(fā)現(xiàn)爬蟲的軌跡凌蔬。因此,我們最好禁用 cookies
在 settings.py 文件中新增以下配置懈词。
# 默認(rèn)是被注釋的, 也就是運(yùn)行使用 cookies
# Disable cookies (enabled by default)
COOKIES_ENABLED = False
3 設(shè)置下載延遲
當(dāng) scrapy 的下載器在下載同一個網(wǎng)站下一個頁面前需要等待的時間辩诞。我們設(shè)置下載延遲, 可以有效避免下載器獲取到下載地址就立刻執(zhí)行下載任務(wù)的情況發(fā)生。從而可以限制爬取速度, 減輕服務(wù)器壓力荞怒。
在 settings.py 文件中新增以下配置秧秉。
# 默認(rèn)是被注釋的
# Configure a delay for requests for the same website (default: 0)
# See http://scrapy.readthedocs.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
DOWNLOAD_DELAY = 3
# 單位是秒, 上述設(shè)置是延遲 3s。
# 同時還支持設(shè)置小數(shù), 例 0.3, 延遲 300 ms
4 設(shè)置代理
有些網(wǎng)站設(shè)置反爬蟲機(jī)制荧嵌,這使得我們的爬蟲程序可能爬到一定數(shù)量網(wǎng)頁就爬取不下去了砾淌。我們需要裝飾下爬蟲,讓它訪問網(wǎng)站行為更像類人行為赃春。使用 IP 代理池能突破大部分網(wǎng)站的限制劫乱。
- 我們可以通過國內(nèi)一些知名代理網(wǎng)站(例如:迅代理衷戈、西刺代理)獲取代理服務(wù)器地址。
我將自己收集一些代理地址以列表形式保存到 settings.py 文件中
# 代理地址具有一定的使用期限, 不保證以下地址都可用殖妇。
PROXY_LIST = [
"https://175.9.77.240:80",
"http://61.135.217.7:80",
"http://113.77.101.113:3128"
"http://121.12.42.180:61234",
"http://58.246.59.59:8080",
"http://27.40.144.98:808",
"https://119.5.177.167:4386",
"https://210.26.54.43:808",
]
- 在 middlewares.py 文件中新建一個名為
ProxyMiddleware
的代理中間層類
import random
from scrapy_demo.settings import PROXY_LIST
class ProxyMiddleware(object):
# overwrite process request
def process_request(self, request, spider):
# Set the location of the proxy
# request.meta['proxy'] = "https://175.9.77.240:80"
request.meta['proxy'] = random.choice(PROXY_LIST)
- 在 settings.py 文件中增加代理配置:
DOWNLOADER_MIDDLEWARES = {
# 第二行的填寫規(guī)則
# yourproject.middlewares(文件名).middleware類
# 設(shè)置代理
'scrapy.contrib.downloadermiddleware.httpproxy.HttpProxyMiddleware': 110,
'scrapy_demo.middlewares.ProxyMiddleware': 100, # scrapy_demo 是你項(xiàng)目的名稱
}
除此之外,如果你比較狠的話座每,可以采用 VPN + Tor 方式來突破反爬蟲機(jī)制摘悴。
5 減小下載超時
如果您對一個非常慢的連接進(jìn)行爬取(一般對通用爬蟲來說并不重要), 減小下載超時能讓卡住的連接能被快速的放棄并解放處理其他站點(diǎn)的能力。
在 settings.py 文件中增加配置:
DOWNLOAD_TIMEOUT = 15
6 頁面跟隨規(guī)則
在爬取網(wǎng)站時叉橱,可能一些頁面是我們不想爬取的者蠕。如果使用 最基本的 Spider,它還是會將這些頁面爬取下來踱侣。因此,我們需要使用更加強(qiáng)大的爬取類CrawlSpider
探膊。
我們的爬取類繼承 CrawlSpider待榔,必須新增定義一個 rules 屬性。rules 是一個包含至少一個 Rule(爬取規(guī)則)對象的 list腌闯。 每個 Rule 對爬取網(wǎng)站的動作定義了特定表現(xiàn)雕憔。CrawlSpider 也是繼承 Spider 類,所以具有Spider的所有函數(shù)分瘦。
Rule 對象的構(gòu)造方法如下:
Rule(link_extractor, callback=None, cb_kwargs=None, follow=None, process_links=None, process_request=None)
我們在使用 Rule 一般只會用到前面幾個參數(shù)畅卓,它們作用如下:
-
link_extractor
: 它是一個 Link Extractor 對象翁潘。 其定義了如何從爬取到的頁面提取鏈接。
link_extractor既可以自己定義渗勘,也可以使用已有LinkExtractor類,主要參數(shù)為:-
allow
:滿足括號中“正則表達(dá)式”的值會被提取乔遮,如果為空取刃,則全部匹配。 -
deny
:與這個正則表達(dá)式(或正則表達(dá)式列表)不匹配的 Url 一定不提取坯辩。 -
allow_domains
:會被提取的鏈接的domains崩侠。 -
deny_domains
:一定不會被提取鏈接的domains。 -
restrict_xpaths
:使用xpath表達(dá)式改抡,和allow共同作用過濾鏈接系瓢。還有一個類似的restrict_css
-
callback
:從 link_extractor 中每獲取到鏈接時將會調(diào)用該函數(shù)。它指定一個回調(diào)方法阵赠。會返回一個包含 Item 對象的列表肌稻。follow
:它 是一個布爾(boolean)值,指定了根據(jù)該規(guī)則從 response 提取的鏈接是否需要跟進(jìn)枷邪。 如果 callback 為None诺凡, follow 默認(rèn)設(shè)置為 True 腹泌,否則默認(rèn)為 False 。process_links
:從link_extractor中獲取到鏈接列表時將會調(diào)用該函數(shù)芥吟。它同樣需要指定一個方法,該方法主要用來過濾 Url钟鸵。
我以爬取豆瓣電影 Top 250 頁面為例子進(jìn)行講解如何利用 rules 進(jìn)行翻頁爬取棺耍。
在頁面的底部,有這樣的分頁俊卤。我們想通過抓取翻頁 url 進(jìn)行下一個頁面爬取害幅。
通過分析頁面可知矫限,鏈接的規(guī)則是
https://movie.douban.com/top250?start=當(dāng)前分頁第一個電影序號&filter=分頁數(shù)
我使用 xpath 來匹配佩抹,當(dāng)然你也可以使用正則表達(dá)式或者 CSS 選擇器。rules 可以這樣定義:
rules = (
Rule(LinkExtractor(allow=(), restrict_xpaths=('//div[@class="paginator"]',)),
follow=True,
callback='parse_item',
process_links='process_links',
),
)
完整的 spider 代碼如下:
# -*- coding: utf-8 -*-
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor
class DoubanTop250(CrawlSpider):
name = 'movie_douban'
allowed_domains = ['douban.com']
start_urls = ["https://movie.douban.com/top250"]
rules = (
Rule(LinkExtractor(allow=(), restrict_xpaths=('//div[@class="paginator"]',)),
follow=True,
callback='parse_item',
process_links='process_links',
),
)
def parse_item(self, response):
# 解析 response, 將其轉(zhuǎn)化為 item
yield item
另外,LinkExtractor 參數(shù)中的 allow() 和 deny() 枢里,我們也是經(jīng)常使用到份乒。規(guī)定爬取哪些頁面是否要進(jìn)行爬取世舰。
7 動態(tài)創(chuàng)建Item類
對于有些應(yīng)用廓奕,item的結(jié)構(gòu)由用戶輸入或者其他變化的情況所控制珊膜。我們可以動態(tài)創(chuàng)建class嚼沿。
from scrapy.item import DictItem, Field
def create_item_class(class_name, field_list):
fields = {
field_name: Field() for field_name in field_list
}
return type(class_name, (DictItem,), {'fields': fields})
系列文章:
學(xué)會運(yùn)用爬蟲框架 Scrapy (一)
學(xué)會運(yùn)用爬蟲框架 Scrapy (二)
學(xué)會運(yùn)用爬蟲框架 Scrapy (四) —— 高效下載圖片
學(xué)會運(yùn)用爬蟲框架 Scrapy (五) —— 部署爬蟲
推薦閱讀:
爬蟲實(shí)戰(zhàn)二:爬取電影天堂的最新電影
爬蟲與反爬蟲的博弈
爬蟲系列的總結(jié)