-
創建一個scrapy項目:scrapy startproject mySpider
-
生成一個爬蟲:scrapy genspider douban movie.douban.com
-
提取數據:完善spider,使用xpath等方法
-
保存數據:pipeline中保存數據
2 創建scrapy項目
下面以抓取豆瓣top250來學習scrapy的入門使用:豆瓣電影 Top 250
安裝scrapy命令:sudo apt-get install scrapyscrapy==2.5.0 或者:pip install scrapy==2.5.0
1.創建項目面板
scrapy 2.5.0 - no active project ? usage:scrapy <command>[options] [args] ? Available commands :bench ? ? ?Run quick benchmark test #測試電腦性能fetch ? ? ?Fetch a URL using the scrapy down1oader#將源代碼下載下來并顯示出來genspider ? ? ?Generate new spider using pre-defined temp1ates#創建一個新的spider文件runspider ? ? ?Run a self-contained spider (without creating a project)# 這個和通過craw1啟動爬蟲不同,scrapy runspider爬蟲文件名稱settings ? ? ?Get settings values#獲取當前的配置信息she11 ? ? ?Interactive scraping console#進入scrapy 的交互模式startproject ? ? ?create new project#創建爬蟲項目version ? ? ?Print scrapy version#顯示scrapy框架的版本view ? ? ?open URL in browser,as seen by scrapy#將網頁document內容下載下來,并且在瀏覽器顯示出來
創建scrapy項目的命令:scrapy startproject +<項目名字>
示例:scrapy startproject myspider
生成的目錄和文件結果如下:
3 創建爬蟲
命令:在項目路徑下執行:scrapy genspider +<爬蟲名字> + <允許爬取的域名>
示例:
cd myspider scrapy genspider douban movie.douban.com
生成的目錄和文件結果如下:
4 完善spider
完善spider即通過方法進行數據的提取等操作
在\myspider\myspider\douban.py中修改內容如下:
import scrapy from scrapy.http import HtmlResponse # 自定義spider類,繼承scrapy.spider class DoubanSpider(scrapy.Spider):# 爬蟲名字name = 'douban'# 允許爬取的范圍,防止爬蟲爬到別的網站allowed_domains = ['douban.com']# 開始爬取的url地址start_urls = ['https://movie.douban.com/top250'] ?# 數據提取的方法,接受下載中間件傳過來的responsedef parse(self, response:HtmlResponse, **kwargs):# scrapy的response對象可以直接進行xpathol_list = response.xpath('//ol[@class="grid_view"]/li')print(ol_list)for ol in ol_list:# 創建一個數據字典item = {}# 利用scrapy封裝好的xpath選擇器定位元素,并通過extract()或extract_first()來獲取結果item['title'] = ol.xpath('.//div[@class="hd"]/a/span[1]/text()').extract_first()item['rating'] = ol.xpath('.//div[@class="bd"]/div/span[2]/text()').extract_first()item['quote'] = ol.xpath('.//div[@class="bd"]//p[@class="quote"]/span/text()').extract_first()print(item)
注意:
-
response.xpath方法的返回結果是一個類似list的類型,其中包含的是selector對象,操作和列表一樣,但是有一些額外的方法
-
extract() 返回一個包含有字符串的列表
-
extract_first() 返回列表中的第一個字符串,列表為空沒有返回None
-
spider中的parse方法必須有
-
需要抓取的url地址必須屬于allowed_domains,但是start_urls中的url地址沒有這個限制
-
啟動爬蟲的時候注意啟動的位置,是在項目路徑下啟動
4.1 response對象屬性
-
url HTTP相應的 URL地址,str類型的
-
status HTTP響應狀態碼,int類型的(在pycharm的控制臺中你可以看到,例如200,404)
-
body HTTP響應正文,bytes類型
-
text 文本形式的HTTP響應正文,str類型,由response.body使用response.encoding解碼得到
-
encoding HTTP響應正文的編碼
-
request 產生該HTTP響應的Requset對象
-
selector (這個比較重要了)選擇器對象用來提取response中的數據
-
xpath(query) 即xml路徑語言,用來確定xml文檔中某部分位置的語言(html屬性xml)
-
css(query) 也是一種選擇器,用來提取頁面內中的數據,但是不如xpath強大。
-
urljoin(url) 用來構造絕對url
?
5 利用管道pipeline來處理(保存)數據
5.1 對douban爬蟲進行修改完善
在爬蟲文件douban.py中parse()函數中最后添加
yield item
注意:yield能夠傳遞的對象只能是:BaseItem,Request,dict,None
5.2 修改pipelines.py文件
class MyspiderPipeline:# 爬蟲文件中提取數據的方法每yield一次item,就會運行一次# 該方法為固定名稱函數def process_item(self, item, spider):print(item)
5.3 在settings.py設置開啟pipeline
ITEM_PIPELINES = {'myspider.pipelines.MyspiderPipeline': 300, } ? # 協議證書配置 # Obey robots.txt rules ROBOTSTXT_OBEY = False ? # 帶上請求頭 # Override the default request headers: DEFAULT_REQUEST_HEADERS = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8','Accept-Language': 'en', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36' }
6 運行scrapy
6.1命令運行
命令:在項目目錄下執行scrapy crawl +<爬蟲名字>
示例:scrapy crawl douban --nolog
6.2 debug運行
# scrapy封裝的命令執行 from scrapy import cmdline ? if __name__ == '__main__':# 解析的是一個列表對象# 獲取的json文件會亂碼 需要修改配置 FEED_EXPORT_ENCODING = 'utf-8'cmdline.execute('scrapy crawl douban -o douban.json -s FEED_EXPORT_ENCODING="utf-8"'.split())
注意:
-
安裝2.5版本之后運行代碼可能會遇到以下錯誤
AttributeError: module 'OpenSSL.SSL' has no attribute 'SSLv3_METHOD'
ImportError: cannot import name 'HTTPClientFactory' from 'twisted.web.client' (unknown location)
-
降低OpenSSL和cryptography以及Twisted的版本
pip install pyOpenSSL==22.0.0 pip install cryptography==38.0.4 pip install Twisted==20.3.0 # 或者 pip install cryptography==3.4.8 pip install pyOpenSSL==21.0.0
-
異步報錯
TypeError: ProactorEventLoop is not supported, got: <ProactorEventLoop running=False closed=False debug=False>
-
這個錯誤通常表示正在嘗試在不支持
ProactorEventLoop
的操作系統上運行Scrapy。Scrapy目前不支持Windows上的ProactorEventLoop
,因此您需要將其更改為SelectorEventLoop
。 -
可以通過在Scrapy項目的settings.py文件中添加以下代碼來更改默認的事件循環
import asyncio if hasattr(asyncio, "WindowsSelectorEventLoopPolicy"):asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
-
2.5的版本適配的是3.7的py版本,需要找到你解釋器對應的版本信息
-
可以運行嘗試
7.輸出的數據日志信息
7.1 日志等級
-
CRITICAL:嚴重錯誤
-
ERROR:一般錯誤
-
WARNING:警告
-
INFO: 一般信息
-
DEBUG:調試信息
注意: 默認的日志等級是DEBUG
7.2日志輸出信息
Versions:使用的工具版本信息 Overridden settings: 重寫的配置 Telnet Password:Telnet 平臺密碼(Scrapy附帶一個內置的telnet控制臺,用于檢查和控制Scrapy運行過程) Enabled extensions :開啟的拓展功能 Enabled downloader middlewares:開啟的下載器中間件 Enabled spider middlewares:開啟的爬蟲中間件 Enabled item pipelines:開啟的管道 Dumping Scrapy stats:所以的信息匯總
7.3 日志等級設置
-
修改settings.py文件
LOG_LEVEL = 'WARNING' # 設置日志顯示的等級為WARNING LOG_FILE = './log.txt' # 將日志信息全部記錄到log.txt文件中
三、scrapy發送翻頁請求
1. 翻頁請求的思路
回顧requests模塊是如何實現翻頁請求的:
-
找到下一頁的URL地址
-
調用requests.get(url)
scrapy實現翻頁的思路:
-
找到下一頁的url地址
-
構造url地址的請求,傳遞給引擎
2 scrapy實現翻頁請求
2.1 實現方法
-
確定url地址
-
構造請求,scrapy.Request(url,callback)
-
callback:指定解析函數名稱,表示該請求返回的響應使用哪一個函數進行解析
-
-
把請求交給引擎:yield scrapy.Request(url,callback)
2.2 豆瓣爬蟲
通過爬取豆瓣top250頁面的電影信息,學習如何實現翻頁請求
地址:豆瓣電影 Top 250
思路分析:
-
獲取首頁的數據
-
尋找下一頁的地址,進行翻頁,獲取數據
注意:
-
可以在settings中設置ROBOTS協議
# False表示忽略網站的robots.txt協議,默認為True ROBOTSTXT_OBEY = False ?
-
可以在settings中設置User-Agent:
# scrapy發送的每一個請求的默認UA都是設置的這個User-Agent USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36' ?
2.3 代碼實現
在爬蟲文件的parse方法中:
# 數據提取的方法,接受下載中間件傳過來的responsedef parse(self, response):# print(response.urljoin('asfd'))# scrapy的response對象可以直接進行xpathol_list = response.xpath('//ol[@class="grid_view"]/li')# 提取下一頁的href并拼接url# print(ol_list)for ol in ol_list:# 創建一個數據字典# item = MyspiderItem()item = {}# 利用scrapy封裝好的xpath選擇器定位元素,并通過extract()或extract_first()來獲取結果item['title'] = ol.xpath('.//div[@class="hd"]/a/span[1]/text()').extract_first()item['rating'] = ol.xpath('.//div[@class="bd"]/div/span[2]/text()').extract_first()item['quote'] = ol.xpath('.//div[@class="bd"]//p[@class="quote"]/span/text()').extract_first()print(item)yield itemif response.xpath("//a[text()='后頁>']/@href").extract_first() != None:next_url = response.urljoin(response.xpath("//a[text()='后頁>']/@href").extract_first())print(next_url)yield scrapy.Request(url=next_url, callback=self.parse)
2.4 scrapy.Request的更多參數
scrapy.Request(url[,callback,method="GET",headers,body,cookies,\ meta,dont_filter=False])
參數解釋
-
中括號中的參數為可選參數
-
callback:表示當前的url的響應交給哪個函數去處理
-
meta:實現數據在不同的解析函數中傳遞,meta默認帶有部分數據,比如下載延遲,請求深度等
-
dont_filter:默認為False,會過濾請求的url地址,即請求過的url地址不會繼續被請求,對需要重復請求的url地址可以把它設置為Ture,比如貼吧的翻頁請求,頁面的數據總是在變化;start_urls中的地址會被反復請求,否則程序不會啟動
-
method:指定POST或GET請求
-
headers:接收一個字典,其中不包括cookies
-
cookies:接收一個字典,專門放置cookies
-
body:接收一個字典,為POST的數據
2.5 重寫start_requests方法
上述方法可以幫助我們實現翻頁的問題,但是這種翻頁并不是框架的最優解,我們可以重寫Spider的start_requests方法,自己生成對應的請求網址給到引擎進行調度
? ?def start_requests(self):for i in range(0, 10):url = 'https://movie.douban.com/top250?start={}&filter='.format(i * 25)yield scrapy.Request(url)
3 meta參數的使用
meta的形式:字典
meta的作用:meta可以實現數據在不同的解析函數中的傳遞
在爬蟲文件的parse方法中,提取詳情頁增加之前callback指定的parse_detail函數:
def parse(self,response):...yield scrapy.Request(detail_url, callback=self.parse_detail,meta={"item":item}) ... ? def parse_detail(self,response):#獲取之前傳入的itemitem = resposne.meta["item"] ?
特別注意
-
meta參數是一個字典
-
meta字典中有一個固定的鍵
proxy
,表示代理ip,關于代理ip的使用我們將在scrapy的下載中間件的學習中進行介紹 -
meta的download_timeout設置請求超時
4. item的使用
4.1 Item能夠做什么
-
定義item即提前規劃好哪些字段需要抓取,scrapy.Field()僅僅是提前占坑,通過item.py能夠讓別人清楚自己的爬蟲是在抓取什么,同時定義好哪些字段是需要抓取的,沒有定義的字段不能使用,防止手誤
-
在python大多數框架中,大多數框架都會自定義自己的數據類型(在python自帶的數據結構基礎上進行封裝),目的是增加功能,增加自定義異常
4.2 定義Item
在items.py文件中定義要提取的字段:
class MyspiderItem(scrapy.Item):title = scrapy.Field() # 標題rating = scrapy.Field() # 評估quote = scrapy.Field() # 概述
4.3 使用Item
Item使用之前需要先導入并且實例化,之后的使用方法和使用字典相同
修改爬蟲文件douban.py:
from myspider.items import MyspiderItem ?# 導入Item,注意路徑 ...def parse(self, response):# print(response.meta['data'])ol_list = response.xpath('//ol[@class="grid_view"]/li') ?for ol in ol_list:item = MyspiderItem()item['title'] = ol.xpath('.//div[@class="hd"]/a/span[1]/text()').extract_first()item['rating'] = ol.xpath('.//div[@class="bd"]/div/span[2]/text()').extract_first()item['quote'] = ol.xpath('.//div[@class="bd"]//p[@class="quote"]/span/text()').extract_first()# item['yeshu'] = response.meta['data']# print(item)yield item
注意:
-
from myspider.items import MyspiderItem這一行代碼中 注意item的正確導入路徑,忽略pycharm標記的錯誤
-
python中的導入路徑要訣:從哪里開始運行,就從哪里開始導入
四、scrapy的管道使用
1. 了解scrapyShell
scrapy shell是scrapy提供的一個終端工具,能夠通過它查看scrapy中對象的屬性和方法,以及測試xpath
使用方法:
scrapy shell https://movie.douban.com/top250
在終端輸入上述命令后,能夠進入python的交互式終端,此時可以使用:
-
response.xpath():直接測試xpath規則是否正確
-
response.url:當前響應的url地址
-
response.request.url:當前響應對應的請求的url地址
-
response.headers:響應頭
-
response.body:響應體,也就是html代碼,默認是byte類型
-
response.requests.headers:當前響應的請求頭
3 settings.py中的設置信息
3.1 為什么項目中需要配置文件
在配置文件中存放一些公共變量,在后續的項目中方便修改,如:本地測試數據庫和部署服務器的數據庫不一致
3.2 配置文件中的變量使用方法
-
變量名一般全部大寫
-
導入即可使用
3.3 settings.py中的重點字段和含義
-
USER_AGENT 設置ua
-
ROBOTSTXT_OBEY 是否遵守robots協議,默認是遵守
-
CONCURRENT_REQUESTS 設置并發請求的數量,默認是16個
-
DOWNLOAD_DELAY 下載延遲,默認無延遲
-
COOKIES_ENABLED 是否開啟cookie,即每次請求帶上前一次的cookie,默認是開啟的
-
DEFAULT_REQUEST_HEADERS 設置默認請求頭
-
SPIDER_MIDDLEWARES 爬蟲中間件,設置過程和管道相同
-
DOWNLOADER_MIDDLEWARES 下載中間件
-
LOG_LEVEL 控制終端輸出信息的log級別,終端默認顯示的是debug級別的log信息
-
LOG_LEVEL = "WARNING"
-
-
LOG_FILE 設置log日志文件的保存路徑,如果設置該參數,終端將不再顯示信息
-
LOG_FILE = "./test.log"
-
-
其他設置參考:Scrapy爬蟲入門教程十三 Settings(設置) - 簡書
4 pipeline管道的深入使用
4.1 pipeline中常用的方法:
-
process_item(self,item,spider):實現對item數據的處理
-
open_spider(self, spider): 在爬蟲開啟的時候僅執行一次
-
close_spider(self, spider): 在爬蟲關閉的時候僅執行一次
4.2 管道文件的修改
在pipelines.py代碼中完善
import pymysql import pymongo ? ? class MyspiderMySQLPipeline:def open_spider(self, spider):# 判斷是哪個爬蟲 名字不同可能執行的爬蟲項目也不同if spider.name == 'douban':self.db = pymysql.connect(host="localhost", user="root", password="root", db="spiders9")self.cursor = self.db.cursor()# 創建變語法sql = '''CREATE TABLE IF NOT EXISTS douban(id int primary key auto_increment not null,quote VARCHAR(255) NOT NULL, rating VARCHAR(255) NOT NULL, title VARCHAR(255) NOT NULL)'''try:self.cursor.execute(sql)print("CREATE TABLE SUCCESS.")except Exception as ex:print(f"CREATE TABLE FAILED,CASE:{ex}") ?def process_item(self, item, spider):# SQL 插入語句sql = 'INSERT INTO douban(id, quote, rating, title) values(%s, %s, %s, %s)'# 執行 SQL 語句try:self.cursor.execute(sql, (0, item['quote'], item['rating'], item['title']))# 提交到數據庫執行self.db.commit()print('mysql數據插入成功...')except Exception as e:print(f'數據插入失敗: {e}')# 如果發生錯誤就回滾self.db.rollback()# 不return的情況下,另一個權重較低的pipeline將不會獲得item,否則后一個pipeline取到的數據為None值return item ?def close_spider(self, spider): ?# 在爬蟲關閉的時候僅執行一次# 關閉文件if spider.name == 'douban':self.db.close() ? ? class MyspiderMongoDBPipeline:def open_spider(self, spider): ?# 在爬蟲開啟的時候僅執行一次if spider.name == 'douban':con = pymongo.MongoClient(host='127.0.0.1', port=27017) ?# 實例化mongoclientself.collection = con.spiders9.douban ?# 創建數據庫名為spiders9,集合名為douban的集合操作對象 ?def process_item(self, item, spider):if spider.name == 'douban':print('mongo保存成功')self.collection.insert_one(dict(item)) ?# 此時item對象需要先轉換為字典,再插入# 不return的情況下,另一個權重較低的pipeline將不會獲得itemreturn item
4.3 開啟管道
在settings.py設置開啟pipeline
ITEM_PIPELINES = {'myspider.pipelines.MyspiderMySQLPipeline': 300,'myspider.pipelines.MyspiderMongoDBPipeline': 400, }
思考:pipeline在settings中能夠開啟多個,為什么需要開啟多個?
-
不同的pipeline可以處理不同爬蟲的數據,通過spider.name屬性來區分
-
不同的pipeline能夠對一個或多個爬蟲進行不同的數據處理的操作,比如一個進行數據清洗,一個進行數據的保存
-
同一個管道類也可以處理不同爬蟲的數據,通過spider.name屬性來區分
4.4 pipeline使用注意點
-
使用之前需要在settings中開啟
-
pipeline在setting中鍵表示位置(即pipeline在項目中的位置可以自定義),值表示距離引擎的遠近,越近數據會越先經過
-
有多個pipeline的時候,process_item的方法必須return item,否則后一個pipeline取到的數據為None值
-
pipeline中process_item的方法必須有,否則item沒有辦法接受和處理
-
process_item方法接受item和spider,其中spider表示當前傳遞item過來的spider
-
open_spider(spider) :能夠在爬蟲開啟的時候執行一次
-
close_spider(spider) :能夠在爬蟲關閉的時候執行一次
-
上述倆個方法經常用于爬蟲和數據庫的交互,在爬蟲開啟的時候建立和數據庫的連接,在爬蟲關閉的時候斷開和數據庫的連接