1. 程式人生 > >Python爬蟲(入門+進階)學習筆記 2-6 Scrapy的Request和Response詳解

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 code
  • handle_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地址的IP
  • dont_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_policy
  • max_retry_times:用於每個請求的重試次數。初始化時,max_retry_times元鍵比RETRY_TIMES設定更高優先順序

Response物件

基礎引數

url——請求的url body——請求回來的html meta——用來在“頁面”之間傳遞資料 headers——頁面的headers資料 cookies——設定頁面的cookies Request——發出這個response的request物件

Response物件方法

copy():同request replace():同request urljoin():由於將頁面相對路徑改為絕對路徑 follow():對相對路徑進行自動補全

urljoin()例項:

  1. import scrapy

  2. class QuotesSpider(scrapy.Spider):

  3. name = "quotes"

  4. start_urls = [

  5. 'http://quotes.toscrape.com/page/1/',

  6. ]

  7. def parse(self, response):

  8. #使用css選擇器,提取出三個元素的目錄的SelectorList

  9. for quote in response.css('div.quote'):

  10. yield {

  11. #使用css選擇器,提取出text元素,並把它轉換成字串

  12. 'text': quote.css('span.text::text').extract_first(),

  13. #使用css選擇器,提取出author元素,並把它轉換成字串

  14. 'author': quote.css('small.author::text').extract_first(),

  15. #使用css選擇器,提取出tags元素,並把它轉換成List

  16. 'tags': quote.css('div.tags a.tag::text').extract(),

  17. }

  18. #使用css選擇器,提取出href元素,並把它轉換成字串

  19. next_page = response.css('li.next a::attr(href)').extract_first()#取出相對路徑

  20. if next_page is not None:

  21. next_page = response.urljoin(next_page) #頁面相對路徑改為絕對路徑

  22. yield scrapy.Request(next_page, callback=self.parse)

follow()例項:

  1. import scrapy

  2. class QuotesSpider(scrapy.Spider):

  3. name = "quotes"

  4. start_urls = [

  5. 'http://quotes.toscrape.com/page/1/',

  6. ]

  7. def parse(self, response):

  8. for quote in response.css('div.quote'):

  9. yield {

  10. 'text': quote.css('span.text::text').extract_first(),

  11. 'author': quote.css('span small::text').extract_first(),

  12. 'tags': quote.css('div.tags a.tag::text').extract(),

  13. }

  14. next_page = response.css('li.next a::attr(href)').extract_first()

  15. if next_page is not None:

  16. yield response.follow(next_page, callback=self.parse) #返回一個請求例項來跟蹤一個連結url

Request、Response例項演示

本節課演示使用的依舊是上節課的58同城(city58)的例子 : city58_test.py:本例中演示了response的follow函式中相對路徑轉化為絕對路徑,並且比較了request中各個引數的異同

  1. # -*- coding: utf-8 -*-

  2. import scrapy

  3. from pyquery import PyQuery

  4. from ..items import City58Item

  5. from scrapy.http import Request

  6. class City58TestSpider(scrapy.Spider):

  7. name = 'city58_test'

  8. allowed_domains = ['58.com']

  9. start_urls = ['http://bj.58.com/chuzu/',

  10. # 'http://bj.58.com/chuzu/pn2/'

  11. ]

  12. def parse(self, response):

  13. jpy = PyQuery(response.text)

  14. li_list = jpy('body > div.mainbox > div.main > div.content > div.listBox > ul > li').items()

  15. for it in li_list:

  16. a_tag = it('div.des > h2 > a')

  17. item = City58Item()

  18. item['name'] = a_tag.text()

  19. item['url'] = a_tag.attr('href')

  20. item['price'] = it('div.listliright > div.money > b').text()

  21. test_request = response.follow('/chuzu/pn2/', callback=self.parse) #使用response.follow方法把“/chuzu/pn2/”這個相對路徑轉換為絕對路徑,並回調parse()函式

  22. test_request2 = Request('http://bj.58.com/chuzu/pn3/',

  23. callback=self.parse,

  24. errback=self.error_back, #呼叫異常函式

  25. cookies={}, #cookie設為空

  26. headers={}, #headers設為空

  27. priority=10

  28. )

  29. test_request3 = Request('http://58.com',

  30. callback=self.parse,

  31. errback=self.error_back, #呼叫異常函式

  32. priority=10, #優先順序設為10

  33. meta={'dont_redirect': True} #不用重定向

  34. )

  35. test_request4 = Request('http://58.com',

  36. callback=self.parse,

  37. errback=self.error_back,

  38. priority=10,

  39. # meta={'dont_redirect': True}

  40. dont_filter=True #對url不過濾

  41. )

  42. yield item

  43. yield test_request

  44. yield test_request2

  45. yield test_request3

  46. yield test_request4

  47. def error_back(self, e):

  48. _ = self

  49. print(e) #列印異常資訊

課後作業:實現58同城的翻頁以及詳情頁的爬取

items.py:定義要爬取的內容

  1. import scrapy

  2. class City58Item(scrapy.Item):

  3. name = scrapy.Field()

  4. price = scrapy.Field()

  5. url = scrapy.Field()

  6. introduce_item = scrapy.Field()

  7. address = scrapy.Field()

  8. phone_number = scrapy.Field()

city58_test.py:實現58同城的翻頁以及詳情頁的爬取

  1. # -*- coding: utf-8 -*-

  2. import scrapy

  3. from pyquery import PyQuery

  4. from ..items import City58Item

  5. from scrapy.http import Request

  6. class City58TestSpider(scrapy.Spider):

  7. name = 'city58_test'

  8. allowed_domains = ['58.com']

  9. start_urls = ['http://bj.58.com/chuzu/']

  10. def parse(self, response):

  11. jpy = PyQuery(response.text)

  12. li_list = jpy('body > div.mainbox > div.main > div.content > div.listBox > ul > li').items()

  13. for it in li_list:

  14. a_tag = it('div.des > h2 > a')

  15. item = City58Item()

  16. item['name'] = a_tag.text()

  17. item['url'] = a_tag.attr('href')

  18. item['price'] = it('div.listliright > div.money > b').text()

  19. if item['url']: #判斷url是否為空

  20. yield Request(item['url'],

  21. callback = self.detail_parse,

  22. meta = {'item':item} , #使用meta引數,把item傳給detail_parse()

  23. priority = 10 , #優先順序設為10

  24. dont_filter=True #強制不過濾)

  25. )

  26. url = jpy('#bottom_ad_li > div.pager > a.next').attr('href') #提取翻頁連結

  27. test_request = Request(url,

  28. callback=self.parse,

  29. priority=10,

  30. # meta={'dont_redirect': True}

  31. dont_filter=True # 對url不過濾

  32. )

  33. yield test_request #實現翻頁

  34. def detail_parse(self,response):

  35. jpy = PyQuery(response.text)

  36. item = response.meta['item'] #接收item

  37. 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() #提取房屋亮點

  38. 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() #房屋詳情地址

  39. 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() #電話號碼

  40. return item

pipeline.py:與以前的例子相同,寫入檔案

  1. import json

  2. class City58Pipeline(object):

  3. def open_spider(self,spider):

  4. self.file = open('58_chuzu.txt', 'w' , encoding='utf8')

  5. print('開啟檔案了')

  6. def process_item(self, item, spider):

  7. line = '{}\n'.format(json.dumps(dict(item),ensure_ascii = False))

  8. self.file.write(line)

  9. return item

  10. def close_spider(self, spider):

  11. self.file.close()

  12. 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引數

  1. import scrapy

  2. class LoginSpider(scrapy.Spider):

  3. name = 'example.com'

  4. start_urls = ['http://www.example.com/users/login.php']

  5. def parse(self, response):

  6. return scrapy.FormRequest.from_response(

  7. response,

  8. formdata={'username': 'john', 'password': 'secret'}, #預先填好的賬號密碼

  9. callback=self.after_login

  10. )

  11. def after_login(self, response):

  12. # check login succeed before going on

  13. if "authentication failed" in response.body:

  14. self.logger.error("Login failed")

  15. return

--------------------- 本文來自 kissazhu 的CSDN 部落格 ,全文地址請點選:https://blog.csdn.net/kissazhu/article/details/80865773?utm_source=copy