您好,登錄后才能下訂單哦!
本文實(shí)例講述了Python利用Scrapy框架爬取豆瓣電影。分享給大家供大家參考,具體如下:
Scrapy是一個(gè)為了爬取網(wǎng)站數(shù)據(jù),提取結(jié)構(gòu)性數(shù)據(jù)而編寫(xiě)的應(yīng)用框架。 可以應(yīng)用在包括數(shù)據(jù)挖掘,信息處理或存儲(chǔ)歷史數(shù)據(jù)等一系列的程序中。
通過(guò)Python包管理工具可以很便捷地對(duì)scrapy進(jìn)行安裝,如果在安裝中報(bào)錯(cuò)提示缺少依賴(lài)的包,那就通過(guò)pip安裝所缺的包
pip install scrapy
scrapy的組成結(jié)構(gòu)如下圖所示
引擎Scrapy Engine,用于中轉(zhuǎn)調(diào)度其他部分的信號(hào)和數(shù)據(jù)傳遞
調(diào)度器Scheduler,一個(gè)存儲(chǔ)Request的隊(duì)列,引擎將請(qǐng)求的連接發(fā)送給Scheduler,它將請(qǐng)求進(jìn)行排隊(duì),但引擎需要時(shí)再將隊(duì)列中的第一個(gè)請(qǐng)求發(fā)送給引擎
下載器Downloader,引擎將請(qǐng)求Request鏈接發(fā)送給Downloader之后它就從互聯(lián)網(wǎng)上下載相應(yīng)的數(shù)據(jù),并將返回的數(shù)據(jù)Responses交給引擎
爬蟲(chóng)Spiders,引擎將下載的Responses數(shù)據(jù)交給Spiders進(jìn)行解析,提取我們需要的網(wǎng)頁(yè)信息。如果在解析中發(fā)現(xiàn)有新的所需要的url連接,Spiders會(huì)將鏈接交給引擎存入調(diào)度器
管道Item Pipline,爬蟲(chóng)會(huì)將頁(yè)面中的數(shù)據(jù)通過(guò)引擎交給管道做進(jìn)一步處理,進(jìn)行過(guò)濾、存儲(chǔ)等操作
下載中間件Downloader Middlewares,自定義擴(kuò)展組件,用于在請(qǐng)求頁(yè)面時(shí)封裝代理、http請(qǐng)求頭等操作
爬蟲(chóng)中間件Spider Middlewares,用于對(duì)進(jìn)入Spiders的Responses和出去的Requests等數(shù)據(jù)作一些修改
scrapy的工作流程:首先我們將入口url交給spider爬蟲(chóng),爬蟲(chóng)通過(guò)引擎將url放入調(diào)度器,經(jīng)調(diào)度器排隊(duì)之后返回第一個(gè)請(qǐng)求Request,引擎再將請(qǐng)求轉(zhuǎn)交給下載器進(jìn)行下載,下載好的數(shù)據(jù)交給爬蟲(chóng)進(jìn)行爬取,爬取的數(shù)據(jù)一部分是我們需要的數(shù)據(jù)交給管道進(jìn)行數(shù)據(jù)清洗和存儲(chǔ),還有一部分是新的url連接會(huì)再次交給調(diào)度器,之后再循環(huán)進(jìn)行數(shù)據(jù)爬取
首先在存放項(xiàng)目的文件夾內(nèi)打開(kāi)命令行,在命令行下輸入scrapy startproject 項(xiàng)目名稱(chēng),就會(huì)在當(dāng)前文件夾自動(dòng)創(chuàng)建項(xiàng)目所需的python文件,例如創(chuàng)建一個(gè)爬取豆瓣電影的項(xiàng)目douban,其目錄結(jié)構(gòu)如下:
Db_Project/ scrapy.cfg --項(xiàng)目的配置文件 douban/ --該項(xiàng)目的python模塊目錄,在其中編寫(xiě)python代碼 __init__.py --python包的初始化文件 items.py --用于定義item數(shù)據(jù)結(jié)構(gòu) pipelines.py --項(xiàng)目中的pipelines文件 settings.py --定義項(xiàng)目的全局設(shè)置,例如下載延遲、并發(fā)量 spiders/ --存放爬蟲(chóng)代碼的包目錄 __init__.py ...
之后進(jìn)入spiders目錄下輸入scrapy genspider 爬蟲(chóng)名 域名,就會(huì)生成爬蟲(chóng)文件douban.py文件,用于之后定義爬蟲(chóng)的爬取邏輯和正則表達(dá)式等內(nèi)容
scrapy genspider douban movie.douban.com
要爬取的豆瓣電影網(wǎng)址為 https://movie.douban.com/top250,其中的每個(gè)電影如下
我們要爬取其中的序號(hào)、名稱(chēng)、介紹、星級(jí)、評(píng)論數(shù)、描述這幾個(gè)關(guān)鍵信息,因此需要在管道文件items.py中先定義這幾個(gè)對(duì)象,類(lèi)似于ORM,通過(guò)scrapy.Field()方法為每個(gè)字段定義一個(gè)數(shù)據(jù)類(lèi)型
import scrapy class DoubanItem(scrapy.Item): ranking = scrapy.Field() # 排名 name = scrapy.Field() # 電影名 introduce = scrapy.Field() # 簡(jiǎn)介 star = scrapy.Field() # 星級(jí) comments = scrapy.Field() # 評(píng)論數(shù) describe = scrapy.Field() # 描述
打開(kāi)之前在spiders文件夾下創(chuàng)建的爬蟲(chóng)文件movie.py如下所示,以及自動(dòng)創(chuàng)建了三個(gè)變量和一個(gè)方法,在parse方法中進(jìn)行返回?cái)?shù)據(jù)response的處理,我們需要在start_urls提供爬蟲(chóng)的入口地址。注意爬蟲(chóng)會(huì)自動(dòng)過(guò)濾掉allowed_domains之外的域名,因此需要注意這個(gè)變量的賦值
# spiders/movie.py import scrapy class MovieSpider(scrapy.Spider): # 爬蟲(chóng)名 name = 'movie' # 允許爬取的域名 allowed_domains = ['movie.douban.com'] # 入口url start_urls = ['https://movie.douban.com/top250'] def parse(self, response): pass
在進(jìn)行數(shù)據(jù)爬取之前首先要設(shè)置一些網(wǎng)絡(luò)代理,在settings.py文件內(nèi)找到USER_AGENT變量修改如下:
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0'
可以在命令行通過(guò)如下命令啟動(dòng)名為douban的爬蟲(chóng):scrapy crawl douban,也可以編寫(xiě)一個(gè)啟動(dòng)文件run.py文件如下,運(yùn)行即可
from scrapy import cmdline cmdline.execute('scrapy crawl movie'.split())
接下來(lái)要對(duì)爬取到的數(shù)據(jù)進(jìn)行過(guò)濾,通過(guò)Xpath規(guī)則可以使我們便捷地選中網(wǎng)頁(yè)中的指定元素,如下圖所示,每個(gè)電影條目都包裹在<ol class="grid_view">下的一個(gè)<li>標(biāo)簽,因此通過(guò)xpath://ol[@class='grid_view']/li 就選中了本頁(yè)面所有的電影條目??梢酝ㄟ^(guò)谷歌瀏覽器的Xpath插件或者火狐瀏覽器的ChroPath獲得xpath值,在瀏覽器右鍵查看元素,彈出如下圖所示的開(kāi)發(fā)者工具,其中最右邊就是ChroPath插件,它直觀地顯示了元素的Xpath值: //div[@id='wrapper']//li
爬蟲(chóng)response對(duì)象的xpath()方法可以直接處理xpath規(guī)則字符串并返回對(duì)應(yīng)的頁(yè)面內(nèi)容,這些內(nèi)容都是選擇器對(duì)象Selector,可以進(jìn)一步作細(xì)化的內(nèi)容選取,通過(guò)xpath選擇出其中的電影名字、簡(jiǎn)介、評(píng)價(jià)、星級(jí)等內(nèi)容,即之前在items.py文件中所定義的數(shù)據(jù)結(jié)構(gòu)DoubanItem。循環(huán)遍歷每個(gè)電影列表從其中爬取到準(zhǔn)確的電影信息,并保存為DoubanItem對(duì)象item,最后通過(guò)yield將item對(duì)象從Spiders返回到Item管道。
爬蟲(chóng)除了從頁(yè)面提取Item數(shù)據(jù)之外還會(huì)爬取url鏈接從而形成下一頁(yè)的Request請(qǐng)求,如下圖所示為豆瓣頁(yè)面底部的下一頁(yè)信息,第二頁(yè)的參數(shù)為"?start=25&filter=",與網(wǎng)站地址https://movie.douban.com/top250拼接起來(lái)就可以得到下一頁(yè)面的地址。和上面一樣通過(guò)xpath提取該內(nèi)容,如果不為空,則拼接得到的Request請(qǐng)求yield提交給調(diào)度器
最終的爬蟲(chóng)movie.py文件如下
# -*- coding: utf-8 -*- import scrapy from items import DoubanItem class MovieSpider(scrapy.Spider): # 爬蟲(chóng)名 name = 'movie' # 爬取網(wǎng)站的域名 allowed_domains = ['movie.douban.com'] # 入口url start_urls = ['https://movie.douban.com/top250'] def parse(self, response): # 首先抓取電影列表 movie_list = response.xpath("http://ol[@class='grid_view']/li") for selector in movie_list: # 遍歷每個(gè)電影列表,從其中精準(zhǔn)抓取所需要的信息并保存為item對(duì)象 item = DoubanItem() item['ranking'] = selector.xpath(".//div[@class='pic']/em/text()").extract_first() item['name'] = selector.xpath(".//span[@class='title']/text()").extract_first() text = selector.xpath(".//div[@class='bd']/p[1]/text()").extract() intro = "" for s in text: # 將簡(jiǎn)介放到一個(gè)字符串 intro += "".join(s.split()) # 去掉空格 item['introduce'] = intro item['star'] = selector.css('.rating_num::text').extract_first() item['comments'] = selector.xpath(".//div[@class='star']/span[4]/text()").extract_first() item['describe'] = selector.xpath(".//span[@class='inq']/text()").extract_first() # print(item) yield item # 將結(jié)果item對(duì)象返回給Item管道 # 爬取網(wǎng)頁(yè)中的下一個(gè)頁(yè)面url信息 next_link = response.xpath("http://span[@class='next']/a[1]/@href").extract_first() if next_link: next_link = "https://movie.douban.com/top250" + next_link print(next_link) # 將Request請(qǐng)求提交給調(diào)度器 yield scrapy.Request(next_link, callback=self.parse)
xpath選擇器
/表示從當(dāng)前位置的下一級(jí)目錄進(jìn)行尋找,//表示從當(dāng)前位置的任意一級(jí)子目錄進(jìn)行尋找,
默認(rèn)從根目錄開(kāi)始查找,. 代表從當(dāng)前目錄開(kāi)始查找,@后跟標(biāo)簽屬性,text()函數(shù)代表取出文字內(nèi)容
//div[@id='wrapper']//li 代表先從根目錄開(kāi)始查找id為wrapper的div標(biāo)簽,然后取出其下的所有l(wèi)i標(biāo)簽
.//div[@class='pic']/em[1]/text() 代表從當(dāng)前選擇器目錄開(kāi)始查找所有class為pic的div之下的第一個(gè)em標(biāo)簽,取出文字內(nèi)容
string(//div[@id='endText']/p[position()>1]) 代表選取id為endText的div下第二個(gè)p標(biāo)簽之后的所有文字內(nèi)容
/bookstore/book[last()-2] 選取屬于 bookstore 子元素的倒數(shù)第3個(gè) book 元素。
CSS選擇器
還可以使用css選擇器來(lái)選擇頁(yè)面內(nèi)的元素,其通過(guò)CSS偽類(lèi)的方式表達(dá)選擇的元素,使用如下
# 選擇類(lèi)名為left的div下的p標(biāo)簽中的文字 response.css('div.left p::text').extract_first() # 選取id為tag的元素下類(lèi)名為star元素中的文字 response.css('#tag .star::text').extract_first()
在運(yùn)行爬蟲(chóng)文件時(shí)通過(guò)參數(shù)-o指定文件保存的位置即可,可以根據(jù)文件后綴名選擇保存為json或者csv文件,例如
scrapy crawl movie -o data.csv
還可以piplines.py文件中對(duì)取得的Item數(shù)據(jù)作進(jìn)一步操作從而通過(guò)python操作將其保存到數(shù)據(jù)庫(kù)中
有時(shí)為了應(yīng)對(duì)網(wǎng)站的反爬蟲(chóng)機(jī)制,需要對(duì)下載中間件進(jìn)行一些偽裝設(shè)置,包括使用IP代理和代理user-agent方式,在middlewares.py文件中新建一個(gè)user_agent類(lèi)用于為請(qǐng)求頭添加用戶(hù)列表,從網(wǎng)上查一些常用的用戶(hù)代理放入U(xiǎn)SER_AGENT_LIST列表,然后通過(guò)random函數(shù)從中隨機(jī)抽取一個(gè)作為代理,設(shè)置為reques請(qǐng)求頭的User_Agent字段
class user_agent(object): def process_request(self, request, spider): # user agent 列表 USER_AGENT_LIST = [ 'MSIE (MSIE 6.0; X11; Linux; i686) Opera 7.23', 'Opera/9.20 (Macintosh; Intel Mac OS X; U; en)', 'Opera/9.0 (Macintosh; PPC Mac OS X; U; en)', 'iTunes/9.0.3 (Macintosh; U; Intel Mac OS X 10_6_2; en-ca)', 'Mozilla/4.76 [en_jp] (X11; U; SunOS 5.8 sun4u)', 'iTunes/4.2 (Macintosh; U; PPC Mac OS X 10.2)', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:5.0) Gecko/20100101 Firefox/5.0', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:9.0) Gecko/20100101 Firefox/9.0', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20120813 Firefox/16.0', 'Mozilla/4.77 [en] (X11; I; IRIX;64 6.5 IP30)', 'Mozilla/4.8 [en] (X11; U; SunOS; 5.7 sun4u)' ] agent = random.choice(USER_AGENT_LIST) # 從上面列表中隨機(jī)抽取一個(gè)代理 request.headers['User_Agent'] = agent # 設(shè)置請(qǐng)求頭的用戶(hù)代理
在settings.py文件內(nèi)設(shè)置開(kāi)啟下載中間件即取消如下幾行的注釋?zhuān)?cè)代理類(lèi)user_agent并設(shè)置優(yōu)先級(jí),數(shù)字越小優(yōu)先級(jí)越高
更多關(guān)于Python相關(guān)內(nèi)容可查看本站專(zhuān)題:《Python Socket編程技巧總結(jié)》、《Python正則表達(dá)式用法總結(jié)》、《Python數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Python函數(shù)使用技巧總結(jié)》、《Python字符串操作技巧匯總》、《Python入門(mén)與進(jìn)階經(jīng)典教程》及《Python文件與目錄操作技巧匯總》
希望本文所述對(duì)大家Python程序設(shè)計(jì)有所幫助。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。