
一:目標(biāo)
使用Scrapy框架遇到很多坑,堅(jiān)持去搜索,修改代碼就可以解決問題。這次爬取的是一個(gè)斗圖網(wǎng)站的最新表情圖片www.doutula.com/photo/list,練習(xí)使用Scrapy框架并且使用的隨機(jī)user agent防止被ban,斗圖表情包每日更新,一共可以抓取5萬張左右的表情到硬盤中。為了節(jié)省時(shí)間我就抓取了1萬多張。
二:Scrapy簡介
Scrapy是一個(gè)為了爬取網(wǎng)站數(shù)據(jù),提取結(jié)構(gòu)性數(shù)據(jù)而編寫的應(yīng)用框架。 可以應(yīng)用在包括數(shù)據(jù)挖掘,信息處理或存儲(chǔ)歷史數(shù)據(jù)等一系列的程序中。
使用過程
- 創(chuàng)建一個(gè)Scrapy項(xiàng)目
- 定義提取的Item
- 編寫爬取網(wǎng)站的 spider 并提取 Item
- 編寫 Item Pipeline 來存儲(chǔ)提取到的Item(即數(shù)據(jù))
接下來的圖表展現(xiàn)了Scrapy的架構(gòu),包括組件及在系統(tǒng)中發(fā)生的數(shù)據(jù)流的概覽(綠色箭頭所示)。 下面對每個(gè)組件都做了簡單介紹,并給出了詳細(xì)內(nèi)容的鏈接。數(shù)據(jù)流如下所描述

** 組件**
Scrapy Engine
引擎負(fù)責(zé)控制數(shù)據(jù)流在系統(tǒng)中所有組件中流動(dòng),并在相應(yīng)動(dòng)作發(fā)生時(shí)觸發(fā)事件。 詳細(xì)內(nèi)容查看下面的數(shù)據(jù)流(Data Flow)部分。調(diào)度器(Scheduler)
調(diào)度器從引擎接受request并將他們?nèi)腙?duì),以便之后引擎請求他們時(shí)提供給引擎。下載器(Downloader)
下載器負(fù)責(zé)獲取頁面數(shù)據(jù)并提供給引擎,而后提供給spider。Spiders
Spider是Scrapy用戶編寫用于分析response并提取item(即獲取到的item)或額外跟進(jìn)的URL的類。 每個(gè)spider負(fù)責(zé)處理一個(gè)特定(或一些)網(wǎng)站。 更多內(nèi)容請看 Spiders 。Item Pipeline
Item Pipeline負(fù)責(zé)處理被spider提取出來的item。典型的處理有清理、 驗(yàn)證及持久化(例如存取到數(shù)據(jù)庫中)。 更多內(nèi)容查看 Item Pipeline 。下載器中間件(Downloader middlewares)
下載器中間件是在引擎及下載器之間的特定鉤子(specific hook),處理Downloader傳遞給引擎的response。 其提供了一個(gè)簡便的機(jī)制,通過插入自定義代碼來擴(kuò)展Scrapy功能。更多內(nèi)容請看 下載器中間件(Downloader Middleware) 。Spider中間件(Spider middlewares)
Spider中間件是在引擎及Spider之間的特定鉤子(specific hook),處理spider的輸入(response)和輸出(items及requests)。 其提供了一個(gè)簡便的機(jī)制,通過插入自定義代碼來擴(kuò)展Scrapy功能。更多內(nèi)容請看 Spider中間件(Middleware) 。
三:實(shí)例分析
1.從網(wǎng)站的主頁進(jìn)入最新斗圖表情后網(wǎng)址是https://www.doutula.com/photo/list/ ,點(diǎn)擊第二頁后看到網(wǎng)址變成了https://www.doutula.com/photo/list/?page=2 ,那我們就知道了網(wǎng)址的構(gòu)成最后的page就是不同的頁數(shù)。那么spider中的start_urls開始入口就如下定義,爬取1到20頁的圖片表情。想下載更多表情頁數(shù)你可以再增加。
start_urls = ['https://www.doutula.com/photo/list/?page={}'.format(i) for i in range(1, 20)]
2.進(jìn)入開發(fā)者模式分析網(wǎng)頁結(jié)構(gòu),可以看到如下結(jié)構(gòu)。右擊復(fù)制一下xpath地址即可得到全部的表情所在的a標(biāo)簽內(nèi)容。a[1]表示第一個(gè)a,去掉[1]就是全部的a。
//*[@id="pic-detail"]/div/div[1]/div[2]/a

值得注意的是這里的表情有兩種:一個(gè)jpg,一個(gè)gif動(dòng)圖。如果獲取圖片地址時(shí)只抓取a標(biāo)簽下面第一個(gè)img的src就會(huì)出錯(cuò),所以我們要抓取img中的含有data-original的值。這里a標(biāo)簽下面還一個(gè)p標(biāo)簽是圖片簡介,我們也抓取下來作為圖片文件的名稱。
圖片的連接是 'http:' + content.xpath('//img/@data-original')
圖片的名稱是 content.xpath('//p/text()')

四:實(shí)戰(zhàn)代碼
完整代碼地址 github.com/rieuse/learnPython
1.首先使用命令行工具輸入代碼創(chuàng)建一個(gè)新的Scrapy項(xiàng)目,之后創(chuàng)建一個(gè)爬蟲。
scrapy startproject ScrapyDoutu
cd ScrapyDoutu\ScrapyDoutu\spiders
scrapy genspider doutula doutula.com
2.打開Doutu文件夾中的items.py,改為以下代碼,定義我們爬取的項(xiàng)目。
import scrapy
class DoutuItem(scrapy.Item):
img_url = scrapy.Field()
name = scrapy.Field()
3.打開spiders文件夾中的doutula.py,改為以下代碼,這個(gè)是爬蟲主程序。
# -*- coding: utf-8 -*-
import os
import scrapy
import requests
from ScrapyDoutu.items import DoutuItems
class Doutu(scrapy.Spider):
name = "doutu"
allowed_domains = ["doutula.com", "sinaimg.cn"]
start_urls = ['https://www.doutula.com/photo/list/?page={}'.format(i) for i in range(1, 40)] # 我們暫且爬取40頁圖片
def parse(self, response):
i = 0
for content in response.xpath('//*[@id="pic-detail"]/div/div[1]/div[2]/a'):
i += 1
item = DoutuItems()
item['img_url'] = 'http:' + content.xpath('//img/@data-original').extract()[i]
item['name'] = content.xpath('//p/text()').extract()[i]
try:
if not os.path.exists('doutu'):
os.makedirs('doutu')
r = requests.get(item['img_url'])
filename = 'doutu\\{}'.format(item['name']) + item['img_url'][-4:]
with open(filename, 'wb') as fo:
fo.write(r.content)
except:
print('Error')
yield item
3.這里面有很多值得注意的部分:
- 因?yàn)閳D片的地址是放在sinaimg.cn中,所以要加入allowed_domains的列表中
-
content.xpath('//img/@data-original').extract()[i]中extract()用來返回一個(gè)list(就是系統(tǒng)自帶的那個(gè)) 里面是一些你提取的內(nèi)容,[i]是結(jié)合前面的i的循環(huán)每次獲取下一個(gè)標(biāo)簽內(nèi)容,如果不這樣設(shè)置,就會(huì)把全部的標(biāo)簽內(nèi)容放入一個(gè)字典的值中。 -
filename = 'doutu\\{}'.format(item['name']) + item['img_url'][-4:]是用來獲取圖片的名稱,最后item['img_url'][-4:]是獲取圖片地址的最后四位這樣就可以保證不同的文件格式使用各自的后綴。 - 最后一點(diǎn)就是如果xpath沒有正確匹配,則會(huì)出現(xiàn)<GET http://*****> (referer: None)
4.配置settings.py,如果想抓取快一點(diǎn)CONCURRENT_REQUESTS設(shè)置大一些,DOWNLOAD_DELAY設(shè)置小一些,或者為0.
# -*- coding: utf-8 -*-
BOT_NAME = 'ScrapyDoutu'
SPIDER_MODULES = ['ScrapyDoutu.spiders']
NEWSPIDER_MODULE = 'ScrapyDoutu.spiders'
DOWNLOADER_MIDDLEWARES = {
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,
'ScrapyDoutu.middlewares.RotateUserAgentMiddleware': 400,
}
ROBOTSTXT_OBEY = False # 不遵循網(wǎng)站的robots.txt策略
CONCURRENT_REQUESTS = 16 #Scrapy downloader 并發(fā)請求(concurrent requests)的最大值
DOWNLOAD_DELAY = 0.2 # 下載同一個(gè)網(wǎng)站頁面前等待的時(shí)間,可以用來限制爬取速度減輕服務(wù)器壓力。
COOKIES_ENABLED = False # 關(guān)閉cookies
5.配置middleware.py配合settings中的UA設(shè)置可以在下載中隨機(jī)選擇UA有一定的反ban效果,在原有代碼基礎(chǔ)上加入下面代碼。這里的user_agent_list可以加入更多。
import random
from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware
class RotateUserAgentMiddleware(UserAgentMiddleware):
def __init__(self, user_agent=''):
self.user_agent = user_agent
def process_request(self, request, spider):
ua = random.choice(self.user_agent_list)
if ua:
print(ua)
request.headers.setdefault('User-Agent', ua)
user_agent_list = [
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1"
"Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5",
"Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"
]
6.到現(xiàn)在為止,代碼都已經(jīng)完成了。那么開始執(zhí)行吧!
scrapy crawl doutu
之后可以看到一邊下載,一邊修改User Agent。

五:總結(jié)
學(xué)習(xí)使用Scrapy遇到很多坑,但是強(qiáng)大的搜索系統(tǒng)不會(huì)讓我感覺孤單。所以感覺Scrapy還是很強(qiáng)大的也很意思,后面繼續(xù)學(xué)習(xí)Scrapy的其他方面內(nèi)容。
貼出我的github地址,我的爬蟲代碼和學(xué)習(xí)的基礎(chǔ)部分都放進(jìn)去了,有喜歡的朋友可以點(diǎn)擊 start follw一起學(xué)習(xí)交流吧!github.com/rieuse/learnPython