Python爬蟲(入門+進階)學習筆記 2-6 Scrapy的Request和Response詳解
上節課我們學習了中介軟體,知道了怎麼通過中介軟體執行反反爬策略。本節課主要介紹Scrapy框架的request物件和response物件
通常,Request物件在爬蟲程式中生成並傳遞到系統,直到它們到達下載程式,後者執行請求並返回一個Response物件,該物件返回到發出請求的爬蟲程式
Request類和Response類都有一些子類,子類用來新增基類中不必要的功能。這些在下面的請求子類和響應子類中描述
Request物件
一個Request物件表示一個HTTP請求,它通常是在爬蟲中生成,並由下載器執行,從而返回Response
基礎引數 :
url
——請求的url
callback
——請求回來的reseponse處理函式,也叫回調函式
meta
——用來在“頁面”之間傳遞資料
- meta是一個dict,主要用來在解析函式之間傳遞值
- 比如:在
parse()
給item某些欄位提取了值,並且提取出了一個新的URL,item另外一些欄位需要在這個新的URL的response裡面提取,為此定義一個parse_item()
解析函式用於處理這個response。在用request傳送這個新的URL請求的時候,使用parse_item()作為回撥函式,並使用meta傳遞原來已經提取的item欄位給parse_item()裡的response- Request物件接受一個meta引數,一個字典物件,同時Response物件有一個meta屬性可以取到相應request傳過來的meta
- 一旦此引數被設定, 通過引數傳遞的字典將會被淺拷貝
headers
——頁面的headers資料
cookies
——設定頁面的cookies
基礎高階引數
encoding
——請求的轉換編碼
priority
——連結優先順序
- 優先順序越高,越優先爬取,但不可以序列化
序列化 (Serialization)
:將物件的狀態資訊轉換為可以儲存或傳輸的形式的過程。在序列化期間,物件將其當前狀態寫入到臨時或永續性儲存區。以後,可以通過從儲存區中讀取或反序列化物件的狀態,重新建立該物件
dont_filter
——強制不過濾 scrapy會對request的URL去重,加上dont_filter則告訴它這個URL不參與去重
errback
——錯誤回掉 errback更適合用於檢查記錄請求產生的錯誤,但是不適合請求的重試
Request物件方法
copy()
:複製一個一模一樣的物件replace()
:對物件引數進行替換
Request.meta 一些特殊的keys
dont_redirect
:如果 Request.meta 包含 dont_redirect 鍵,則該request將會被RedirectMiddleware忽略dont_retry
:如果 Request.meta 包含 dont_retry 鍵, 該request將會被RetryMiddleware忽略handle_httpstatus_list
:Request.meta 中的 handle_httpstatus_list 鍵可以用來指定每個request所允許的response codehandle_httpstatus_all
:handle_httpstatus_all為True ,可以允許請求的任何響應程式碼dont_merge_cookies
:Request.meta 中的dont_merge_cookies設為TRUE,可以避免與現有cookie合併cookiejar
:Scrapy通過使用 Request.meta中的cookiejar 來支援單spider追蹤多cookie session。 預設情況下其使用一個cookie jar(session),不過可以傳遞一個標示符來使用多個dont_cache
:可以避免使用dont_cache元鍵等於True快取每個策略的響應redirect_urls
:通過該中介軟體的(被重定向的)request的url可以通過 Request.meta 的 redirect_urls 鍵找到bindaddress
:用於執行請求的傳出IP地址的IPdont_obey_robotstxt
:如果Request.meta將dont_obey_robotstxt鍵設定為True,則即使啟用ROBOTSTXT_OBEY,RobotsTxtMiddleware也會忽略該請求download_timeout
:下載器在超時之前等待的時間(以秒為單位)download_maxsize
:爬取URL的最大長度download_latency
:自請求已經開始,即通過網路傳送的HTTP訊息,用於獲取響應的時間量 該元金鑰僅在下載響應時才可用。雖然大多數其他元鍵用於控制Scrapy行為,但是這個應用程式應該是隻讀的download_fail_on_dataloss
:是否在故障響應失敗proxy
:可以將代理每個請求設定為像http:// some_proxy_server:port這樣的值ftp_user
:用於FTP連線的使用者名稱ftp_password
:用於FTP連線的密碼referrer_policy
:為每個請求設定referrer_policymax_retry_times
:用於每個請求的重試次數。初始化時,max_retry_times元鍵比RETRY_TIMES設定更高優先順序
Response物件
基礎引數
url
——請求的urlbody
——請求回來的htmlmeta
——用來在“頁面”之間傳遞資料headers
——頁面的headers資料cookies
——設定頁面的cookiesRequest
——發出這個response的request物件
Response物件方法
copy()
:同requestreplace()
:同requesturljoin()
:由於將頁面相對路徑改為絕對路徑follow()
:對相對路徑進行自動補全
urljoin()例項:
-
import scrapy
-
class QuotesSpider(scrapy.Spider):
-
name = "quotes"
-
start_urls = [
-
'http://quotes.toscrape.com/page/1/',
-
]
-
def parse(self, response):
-
#使用css選擇器,提取出三個元素的目錄的SelectorList
-
for quote in response.css('div.quote'):
-
yield {
-
#使用css選擇器,提取出text元素,並把它轉換成字串
-
'text': quote.css('span.text::text').extract_first(),
-
#使用css選擇器,提取出author元素,並把它轉換成字串
-
'author': quote.css('small.author::text').extract_first(),
-
#使用css選擇器,提取出tags元素,並把它轉換成List
-
'tags': quote.css('div.tags a.tag::text').extract(),
-
}
-
#使用css選擇器,提取出href元素,並把它轉換成字串
-
next_page = response.css('li.next a::attr(href)').extract_first()#取出相對路徑
-
if next_page is not None:
-
next_page = response.urljoin(next_page) #頁面相對路徑改為絕對路徑
-
yield scrapy.Request(next_page, callback=self.parse)
follow()例項:
-
import scrapy
-
class QuotesSpider(scrapy.Spider):
-
name = "quotes"
-
start_urls = [
-
'http://quotes.toscrape.com/page/1/',
-
]
-
def parse(self, response):
-
for quote in response.css('div.quote'):
-
yield {
-
'text': quote.css('span.text::text').extract_first(),
-
'author': quote.css('span small::text').extract_first(),
-
'tags': quote.css('div.tags a.tag::text').extract(),
-
}
-
next_page = response.css('li.next a::attr(href)').extract_first()
-
if next_page is not None:
-
yield response.follow(next_page, callback=self.parse) #返回一個請求例項來跟蹤一個連結url
Request、Response例項演示
本節課演示使用的依舊是上節課的58同城(city58)的例子 : city58_test.py
:本例中演示了response的follow函式中相對路徑轉化為絕對路徑,並且比較了request中各個引數的異同
-
# -*- coding: utf-8 -*-
-
import scrapy
-
from pyquery import PyQuery
-
from ..items import City58Item
-
from scrapy.http import Request
-
class City58TestSpider(scrapy.Spider):
-
name = 'city58_test'
-
allowed_domains = ['58.com']
-
start_urls = ['http://bj.58.com/chuzu/',
-
# 'http://bj.58.com/chuzu/pn2/'
-
]
-
def parse(self, response):
-
jpy = PyQuery(response.text)
-
li_list = jpy('body > div.mainbox > div.main > div.content > div.listBox > ul > li').items()
-
for it in li_list:
-
a_tag = it('div.des > h2 > a')
-
item = City58Item()
-
item['name'] = a_tag.text()
-
item['url'] = a_tag.attr('href')
-
item['price'] = it('div.listliright > div.money > b').text()
-
test_request = response.follow('/chuzu/pn2/', callback=self.parse) #使用response.follow方法把“/chuzu/pn2/”這個相對路徑轉換為絕對路徑,並回調parse()函式
-
test_request2 = Request('http://bj.58.com/chuzu/pn3/',
-
callback=self.parse,
-
errback=self.error_back, #呼叫異常函式
-
cookies={}, #cookie設為空
-
headers={}, #headers設為空
-
priority=10
-
)
-
test_request3 = Request('http://58.com',
-
callback=self.parse,
-
errback=self.error_back, #呼叫異常函式
-
priority=10, #優先順序設為10
-
meta={'dont_redirect': True} #不用重定向
-
)
-
test_request4 = Request('http://58.com',
-
callback=self.parse,
-
errback=self.error_back,
-
priority=10,
-
# meta={'dont_redirect': True}
-
dont_filter=True #對url不過濾
-
)
-
yield item
-
yield test_request
-
yield test_request2
-
yield test_request3
-
yield test_request4
-
def error_back(self, e):
-
_ = self
-
print(e) #列印異常資訊
課後作業:實現58同城的翻頁以及詳情頁的爬取
items.py
:定義要爬取的內容
-
import scrapy
-
class City58Item(scrapy.Item):
-
name = scrapy.Field()
-
price = scrapy.Field()
-
url = scrapy.Field()
-
introduce_item = scrapy.Field()
-
address = scrapy.Field()
-
phone_number = scrapy.Field()
city58_test.py
:實現58同城的翻頁以及詳情頁的爬取
-
# -*- coding: utf-8 -*-
-
import scrapy
-
from pyquery import PyQuery
-
from ..items import City58Item
-
from scrapy.http import Request
-
class City58TestSpider(scrapy.Spider):
-
name = 'city58_test'
-
allowed_domains = ['58.com']
-
start_urls = ['http://bj.58.com/chuzu/']
-
def parse(self, response):
-
jpy = PyQuery(response.text)
-
li_list = jpy('body > div.mainbox > div.main > div.content > div.listBox > ul > li').items()
-
for it in li_list:
-
a_tag = it('div.des > h2 > a')
-
item = City58Item()
-
item['name'] = a_tag.text()
-
item['url'] = a_tag.attr('href')
-
item['price'] = it('div.listliright > div.money > b').text()
-
if item['url']: #判斷url是否為空
-
yield Request(item['url'],
-
callback = self.detail_parse,
-
meta = {'item':item} , #使用meta引數,把item傳給detail_parse()
-
priority = 10 , #優先順序設為10
-
dont_filter=True #強制不過濾)
-
)
-
url = jpy('#bottom_ad_li > div.pager > a.next').attr('href') #提取翻頁連結
-
test_request = Request(url,
-
callback=self.parse,
-
priority=10,
-
# meta={'dont_redirect': True}
-
dont_filter=True # 對url不過濾
-
)
-
yield test_request #實現翻頁
-
def detail_parse(self,response):
-
jpy = PyQuery(response.text)
-
item = response.meta['item'] #接收item
-
item['introduce_item'] = jpy('body > div.main-wrap > div.house-detail-desc > div.main-detail-info.fl > div.house-word-introduce.f16.c_555 > ul > li:nth-child(1) > span.a2').text() #提取房屋亮點
-
item['address'] = jpy('body > div.main-wrap > div.house-basic-info > div.house-basic-right.fr > div.house-basic-desc > div.house-desc-item.fl.c_333 > ul > li:nth-child(6) > span.dz').text() #房屋詳情地址
-
item['phone_number'] = jpy('body > div.main-wrap > div.house-basic-info > div.house-basic-right.fr > div.house-fraud-tip > div.house-chat-phone > span').text() #電話號碼
-
return item
pipeline.py
:與以前的例子相同,寫入檔案
-
import json
-
class City58Pipeline(object):
-
def open_spider(self,spider):
-
self.file = open('58_chuzu.txt', 'w' , encoding='utf8')
-
print('開啟檔案了')
-
def process_item(self, item, spider):
-
line = '{}\n'.format(json.dumps(dict(item),ensure_ascii = False))
-
self.file.write(line)
-
return item
-
def close_spider(self, spider):
-
self.file.close()
-
print('關閉檔案了')
補充資料
使用FormRequest.from_response()方法模擬使用者登入:
FormRequest類擴充套件了基本請求,具有處理HTML表單的功能。它使用lxml.html表單從表單資料預先填充表單域從響應物件
除了標準的Request方法之外,FormRequest物件還支援以下類方法:
classmethod from_response(response[, formname=None, formid=None, formnumber=0, formdata=None, formxpath=None, formcss=None, clickdata=None, dont_click=False, ...])
返回一個新的FormRequest物件,其表單欄位值預先填充在給定響應中包含的HTML <form>
元素中
該policy是預設情況下自動模擬任何可以點選的表單控制元件,如 < input type =“submit” >。即使這是非常方便的,通常是期望的行為,有時它可能會導致難以除錯的問題。例如,使用javascript填充和/或提交的表單時,預設的from_response()行為可能不是最合適的。要禁用此行為,您可以將dont_click引數設定為True。另外,如果要更改點選的控制元件(而不是禁用它),還可以使用clickdata引數
-
import scrapy
-
class LoginSpider(scrapy.Spider):
-
name = 'example.com'
-
start_urls = ['http://www.example.com/users/login.php']
-
def parse(self, response):
-
return scrapy.FormRequest.from_response(
-
response,
-
formdata={'username': 'john', 'password': 'secret'}, #預先填好的賬號密碼
-
callback=self.after_login
-
)
-
def after_login(self, response):
-
# check login succeed before going on
-
if "authentication failed" in response.body:
-
self.logger.error("Login failed")
-
return
--------------------- 本文來自 kissazhu 的CSDN 部落格 ,全文地址請點選:https://blog.csdn.net/kissazhu/article/details/80865773?utm_source=copy