?本文作者:陳 鼎 中南財經(jīng)政法大學(xué)統(tǒng)計與數(shù)學(xué)學(xué)院
文字編輯:任 哲
技術(shù)總編:張馨月
一、前言
??scrapy是基于twisted的異步處理框架瑟幕,與傳統(tǒng)的requests爬蟲程序執(zhí)行流程不同合冀,scrapy使用多線程窟却,將發(fā)送請求刽酱,提取數(shù)據(jù)臭蚁,保存數(shù)據(jù)等操作分別交給Scheduler(調(diào)度器)载城,Downloader(下載器)肌似,Spider(爬蟲),Pipeline(管道)等爬蟲“組件”來完成诉瓦。多線程的運行框架使得爬蟲的效率大大提升川队,讓爬蟲程序變得更快,更強睬澡」潭睿基于以上特點,本文將以爬取豆瓣圖書信息為例煞聪,簡要闡述基于scrapy框架下的爬蟲實現(xiàn)流程斗躏。
二、爬蟲流程以及代碼實現(xiàn)
(一)分析需要爬取的網(wǎng)頁結(jié)構(gòu)
??在編寫一個爬蟲項目之前昔脯,我們需要對所需爬取的網(wǎng)頁有一個清晰的認識啄糙。爬蟲的本質(zhì)是在響應(yīng)中的字符串提取所需信息,即只有我們提取到的響應(yīng)中存在我們所需要的數(shù)據(jù)時云稚,我們才能進行爬蟲隧饼。我們訪問豆瓣讀書(https://book.douban.com/tag/?view=type),發(fā)現(xiàn)豆瓣圖書標簽中存在許多大分類(文學(xué)静陈,文化...)燕雁,大分類中存在許多小分類(小說,外國文學(xué)...)鲸拥。點開每個小分類標簽拐格,會呈現(xiàn)出不同類型的書的列表清單,且不止一頁刑赶。我們要做的就是提取豆瓣所有類型書籍下的所有書籍的簡要信息禁荒,包括圖書作者,書名角撞,圖書價格呛伴,豆瓣評分勃痴,書籍評論人數(shù)等。網(wǎng)頁的頁面如下圖所示:
(二)創(chuàng)建scrapy項目
??創(chuàng)建scrapy項目十分簡單热康,首先打開命令提示符耐版,通過cd命令路徑抬闯,將工作路徑定位到我們需要創(chuàng)建項目的路徑下,然后創(chuàng)建一個scrapy項目,用到的程序如下:
scrapy startproject douban_books #創(chuàng)建一個名字為douban_books的爬蟲項目
cd douban_books #定位到項目文件夾內(nèi)
scrapy genspider book book.douban.com #創(chuàng)建爬蟲所需的腳本文件book.py细睡;book.douban.com設(shè)置允許爬取的網(wǎng)頁范圍(allow_domains)
(三)設(shè)置USER_AGENT颈将,LOG_LEVEL
??接下里我們切換到settings.py文件中挤茄,對爬蟲項目進行變量配置與賦值逞刷。首先,利用url地址請求頭中的USER_AGENT對發(fā)送請求進行偽裝惊暴,可更加順利地發(fā)送請求并獲取到服務(wù)器的響應(yīng)饼丘。用LOG_LEVEL設(shè)置的日志級別,讓打印出來的結(jié)果更加干凈辽话,整潔肄鸽。
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36' #設(shè)置useragent
LOG_LEVEL = 'WARNING' #設(shè)置日志級別,即輸出結(jié)果只會顯示warning以及warning以上的日志
(四)編寫爬蟲程序
??打開spiders文件夾下的book.py文件油啤,我們將在此文件中編寫實現(xiàn)提取數(shù)據(jù)的核心代碼典徘。
??在此程序中,start_url為我們首先要發(fā)送的url地址益咬,該url地址不受allowed_domains約束逮诲;parse函數(shù)用來執(zhí)行提取start_url頁面數(shù)據(jù)的主要邏輯,需要注意幽告,該函數(shù)名不可以隨意更改梅鹦。如下:
import scrapy
import re
from copy import deepcopy
class BookSpider(scrapy.Spider):
name = 'book'#爬蟲名
allowed_domains = ['book.douban.com'] #允許爬取的url地址范圍
start_urls = ['https://book.douban.com/tag/?view=type'] #首先發(fā)送請求的url地址
def parse(self, response): #實現(xiàn)提取數(shù)據(jù)等主要邏輯
pass
??程序的基本框架搭建好后,就可以開始編寫獲取網(wǎng)頁信息的程序评腺。
1. 提取豆瓣圖書的大標簽帘瞭,小標簽
??在開發(fā)者工具的elements界面淑掌,可以通過對網(wǎng)頁的觀察定位到所需信息的xpath并進行相應(yīng)提取蒿讥。此外,大家也可使用爬蟲利器xpath helper進行定位抛腕。
??通過網(wǎng)頁標簽分析可以看出芋绸,大標題(小說,外國文學(xué)...)對應(yīng)了六塊大標簽担敌,每塊大標簽下存放了以行捆綁的中標簽摔敛,中標簽下才是我們所需要提取的小標簽(文學(xué),文化...)全封。因此想要提取到所有數(shù)據(jù)马昙,我們需要對響應(yīng)進行三次遍歷操作桃犬。
def parse(self, response):
item = {}
div_list = response.xpath(".//div[@class='article']/div[2]/div") # 進行分組
for div in div_list:
item["big_title"] = div.xpath("./a/@name").extract_first() # 提取大標簽
tr_list = div.xpath(".//table[@class='tagCol']") # 進行分組
for tr in tr_list:
td_list = tr.xpath(".//td")
for td in td_list:
item["small_title"] = td.xpath("./a/text()").extract_first()
item["cate_list_url"] = td.xpath("./a/@href").extract_first()
2.發(fā)送每個小標簽地址的請求行楞,獲取每個小標簽url地址的響應(yīng)
??從圖7可以看出攒暇,我們抓取的url地址不完整,因此需要對其進行補充子房,再分別發(fā)送請求形用。
if item["cate_list_url"] is not None:
item["cate_list_url"] = 'https://book.douban.com' + item["cate_list_url"]
yield scrapy.Request(
item["cate_list_url"],
callback=self.parse_list,
meta={"item": deepcopy(item)}
)
3. 提取書籍的數(shù)據(jù)
??在第2節(jié)中我們獲取了每個小標簽url地址的響應(yīng),接下來只需要新定義一個函數(shù)parse_list來處理響應(yīng)证杭,就可以抓取到所需數(shù)據(jù)田度。而這些數(shù)據(jù)中往往帶有換行符、制表符以及空格等我們所不需要的字符解愤,因此可以使用正則表達式進行處理镇饺,使最終提取的數(shù)據(jù)更為美觀。
def parse_list(self, response):
item = response.meta["item"]
li_list = response.xpath(".//ul[@class='subject-list']/li") # 分組
for li in li_list:
item["book_name"] = li.xpath(".//div[@class='info']/h2/a/@title").extract_first()
item["book_name"] = re.sub(r"[(\n)(\t)( )]", "", item["book_name"]) #刪除書名中的空格與換行符等
item["book_score"] = li.xpath(".//div[@class='star clearfix']/span[@class='rating_nums']/text()").extract_first()
book_detail_str = li.xpath(".//div[@class='info']//div[@class='pub']/text()").extract_first()
book_detail_str = re.sub(r"[(\n)( )]", "", book_detail_str) #提取書籍簡要信息琢歇,并對簡要信息進行切片處理兰怠,提取切片中的內(nèi)容
book_detail_list = list(book_detail_str.split("/"))
item["book_price"] = book_detail_list[-1] if len(book_detail_list) > 0 else None
item["book_author"] = book_detail_list[0] if len(book_detail_list) > 0 else None
item["book_comment_nums"] = li.xpath(".//div[@class='star clearfix']/span[@class='pl']/text()").extract_first()
item["book_comment_nums"] = re.sub(r"[(\n)( )]", "", item["book_comment_nums"])
print(item)
4 實現(xiàn)翻頁請求
??通過對網(wǎng)頁結(jié)構(gòu)進行分析,小編發(fā)現(xiàn)每一個小便簽下的圖書信息不止一頁李茫,因此需要設(shè)置翻頁請求揭保,定位到“下一頁”按鈕標簽后,同樣發(fā)現(xiàn)抓取的url地址不全魄宏,需要將地址補全秸侣。小編將發(fā)出翻頁請求所對應(yīng)的響應(yīng)(即callback)也放入parse_list函數(shù)。
next_page = response.xpath(".//span[@class='next']/a/@href").extract_first()#提取url地址
if next_page is not None:#判斷宠互,如果還有下一頁味榛,就繼續(xù)發(fā)送請求
next_page = 'https://book.douban.com' + next_page
yield scrapy.Request(
next_page,
callback=self.parse_list,#把發(fā)出的請求交給parse_list函數(shù)進行處理
meta = {"item":deepcopy(item)}
)
yield item
5. 開啟管道,保存數(shù)據(jù)
??管道用于將爬取的數(shù)據(jù)保存到本地文件或數(shù)據(jù)庫中予跌。管道需要事先在settings.py文件中開啟搏色,將# Configure item pipelines下的注釋行解除,便可在pipeline.py中實現(xiàn)保存功能券册,本文以創(chuàng)建一個.txt文檔對數(shù)據(jù)進行保存為例频轿。
import json
class DoubanBooksPipeline:
def process_item(self, item, spider):
with open("douban_book_list.txt","a",encoding="utf-8") as f:
f.write(json.dumps(item,ensure_ascii=False))
6. 運行項目,爬取數(shù)據(jù)
??完成上述設(shè)置后烁焙,就可運行項目抓取數(shù)據(jù)啦~運行項目僅需打開命令提示符航邢,輸入如下程序即可執(zhí)行:
scrapy crawl book
三、注意事項
??1.deepcopy是用于不同函數(shù)在多線程傳輸之中進行備份操作的行為骄蝇。由于數(shù)據(jù)公用一個item字典膳殷,在多線程操作中可能出現(xiàn)重復(fù)值的現(xiàn)象,利用deepcopy可以有效地解決item中出現(xiàn)重復(fù)值的問題九火。
??2.豆瓣具備一定反爬蟲赚窃,scrapy可能用同一個headers運行數(shù)秒就會被服務(wù)器監(jiān)測為爬蟲行為册招,需要利用一些反反爬蟲的操作。例如:在settings.py中設(shè)置一個USER_AGENT和IP池勒极,并在發(fā)送請求時隨機選擇一個USER_AGENT和IP跨细,便可以進行有效地偽裝,達到更完善的爬蟲效果河质。
??(ps:需要完整的程序可以關(guān)注微信公眾號:Stata and Python數(shù)據(jù)分析冀惭,并在后臺回復(fù)“豆瓣圖書”來獲取~)