scrapy之遞歸解析(爬取多頁頁面數據)
1.遞歸爬取解析多頁頁面數據
- 需求:將糗事百科所有頁碼的作者和段子內容數據進行爬取切持久化存儲
- 需求分析:每一個頁面對應一個url,則scrapy工程需要對每一個頁碼對應的url依次發起請求,然后通過對應的解析方法進行作者和段子內容的解析。
實現方案:
1.將每一個頁碼對應的url存放到爬蟲文件的起始url列表(start_urls)中。(不推薦)
2.使用Request方法手動發起請求。(推薦)
# -*- coding: utf-8 -*- import scrapy from qiushibaike.items import QiushibaikeItem # scrapy.http import Request class QiushiSpider(scrapy.Spider): name = 'qiushi' allowed_domains = ['www.qiushibaike.com'] start_urls = ['https://www.qiushibaike.com/text/'] #爬取多頁 pageNum = 1 #起始頁碼 url = 'https://www.qiushibaike.com/text/page/%s/' #每頁的url def parse(self, response): div_list=response.xpath('//*[@id="content-left"]/div') for div in div_list: #//*[@id="qiushi_tag_120996995"]/div[1]/a[2]/h2 author=div.xpath('.//div[@class="author clearfix"]//h2/text()').extract_first() author=author.strip('\n') content=div.xpath('.//div[@class="content"]/span/text()').extract_first() content=content.strip('\n') item=QiushibaikeItem() item['author']=author item['content']=content yield item #提交item到管道進行持久化 #爬取所有頁碼數據 if self.pageNum <= 13: #一共爬取13頁(共13頁) self.pageNum += 1 url = format(self.url % self.pageNum) #遞歸爬取數據:callback參數的值為回調函數(將url請求后,得到的相應數據繼續進行parse解析),遞歸調用parse函數 yield scrapy.Request(url=url,callback=self.parse) #發送的是get請求,多頁面解析過程一樣,所以回調函數是parse()
scrapy之主動發送請求(get、post)(item傳參數)
scrapy.Request() 發送的是get請求
scrapy.FormRequest() 發送的是post請求
示例1:爬取某電影網站中電影名稱和電影詳情頁中的導演信息(發送get請求,傳item參數,封裝到item對象中)
- move.py
# -*- coding: utf-8 -*- import scrapy from moviePro.items import MovieproItem class MovieSpider(scrapy.Spider): name = 'movie' # allowed_domains = ['www.xxx.com'] start_urls = ['https://www.4567tv.tv/frim/index1.html'] #解析詳情頁中的數據 def parse_detail(self,response): #response.meta返回接收到的meta字典 item = response.meta['item'] actor = response.xpath('/html/body/div[1]/div/div/div/div[2]/p[3]/a/text()').extract_first() item['actor'] = actor yield item def parse(self, response): li_list = response.xpath('//li[@class="col-md-6 col-sm-4 col-xs-3"]') for li in li_list: item = MovieproItem() name = li.xpath('./div/a/@title').extract_first() detail_url = 'https://www.4567tv.tv'+li.xpath('./div/a/@href').extract_first() item['name'] = name #meta參數:請求傳參.meta字典就會傳遞給回調函數的response參數 yield scrapy.Request(url=detail_url,callback=self.parse_detail,meta={'item':item})
- items.py
import scrapy class MoveproItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() title = scrapy.Field() actor = scrapy.Field()
- settings.py
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36' # Obey robots.txt rules ROBOTSTXT_OBEY = False
示例2:百度翻譯中翻譯“dog”,發送的是post請求(重寫 start_requests() 方法)
# -*- coding: utf-8 -*- import scrapy class PostSpider(scrapy.Spider): name = 'post' # allowed_domains = ['www.xxx.com'] start_urls = ['https://fanyi.baidu.com/sug'] def start_requests(self): # 重寫該方法,發送的是post請求,scrapy.FormRequest() data = { 'kw':'dog' } for url in self.start_urls: # yield scrapy.Request(url=url,callback=self.parse) #本來該函數封裝的是對url的get請求,scrapy.Request() yield scrapy.FormRequest(url=url,callback=self.parse,formdata=data) #這里重寫, def parse(self, response): print(response.text)
備注:
1、parse(self,response) 中的response,默認是start_urls中 get 請求后,返回的對象
2、要發送post請求,需要重寫start_requests(self)方法,使用 scrapy.FormRequest()
3、get請求,scrapy.Request()
scrapy之日志等級
- 在使用scrapy crawl spiderFileName運行程序時,在終端里打印輸出的就是scrapy的日志信息。
- 日志信息的種類:
ERROR : 一般錯誤
WARNING : 警告
INFO : 一般的信息
DEBUG : 調試信息
- 設置日志信息指定輸出:
在settings.py配置文件中,加入
LOG_LEVEL = ‘指定日志信息種類’即可。
LOG_FILE = 'log.txt'則表示將日志信息寫入到指定文件中進行存儲
- settings.py
LOG_LEVEL = "ERROR" #添加之后,只有出錯的時候才會打印錯誤信息
scrapy之中間件 !!!
!!!! 中間件的使用一定要在settings.py中添加類,不然無法執行。
一.下載中間件
先祭出框架圖:
解釋如下:
- 引擎(Scrapy)
用來處理整個系統的數據流處理,觸發事務(框架核心)
- 調度器(Scheduler)
用來接收引擎發過來的請求,壓入隊列中,並在引擎再次請求的時候返回,可以想象成一個URL(抓取網頁的網址或者說是鏈接)的優先隊列,由它來決定下一個要抓取的網址是什么,同時去除重復的網址
- 下載器(Downloader)
用於下載網頁內容,並將網頁內容返回給蜘蛛(Scrapy下載器是建立在twisted這個高效的異步模型上的)
- 爬蟲(Spiders)
爬蟲是主要干活的,用於從特定的網頁中提取自己需要的信息,即所謂的實體(Item)。用戶也可以從中提取出鏈接,讓scrapy繼續抓取下一個頁面
- 項目管道(Pipeline)
負責處理爬蟲從網頁中抽取的實體,主要的功能是持久化實體,驗證實體的有效性,清除不需要的信息。當頁面被爬蟲解析后,將被發送到項目管道,並經過幾個特定的次序處理數據
下載中間件(Downloader Middlewares) 位於scrapy引擎和下載器之間的一層組件。
- 作用:
(1)引擎將請求傳遞給下載器過程中, 下載中間件可以對請求進行一系列處理。比如設置請求的 User-Agent,設置代理等
(2)在下載器完成將Response傳遞給引擎中,下載中間件可以對響應進行一系列處理。比如進行gzip解壓等。
我們主要使用下載中間件處理請求,一般會對請求設置隨機的User-Agent ,設置隨機的代理。目的在於防止爬取網站的反爬蟲策略。
二、UA池(User-Agent)和代理池(使用中間件實現)
1、UA池:User-Agent池
- 作用:盡可能多的將scrapy工程中的請求偽裝成不同類型的瀏覽器身份。
- 操作流程:
1.在下載中間件中攔截請求
2.將攔截到的請求的請求頭信息中的UA進行篡改偽裝
3.在配置文件中開啟下載中間件
2、代理池
- 作用:盡可能多的將scrapy工程中的請求的IP設置成不同的。
- 操作流程:
1.在下載中間件中攔截請求
2.將攔截到的請求的IP修改成某一代理IP
3.在配置文件中開啟下載中間件
# middles.py
import random # 中間件要寫在下載中間件中 class MiddleproDownloaderMiddleware(object): # Not all methods need to be defined. If a method is not defined, # scrapy acts as if the downloader middleware does not modify the # passed objects. @classmethod def from_crawler(cls, crawler): # This method is used by Scrapy to create your spiders. s = cls() crawler.signals.connect(s.spider_opened, signal=signals.spider_opened) return s user_agent_list = [ "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 " "(KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1", "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 " "(KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 " "(KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6", "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 " "(KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6", "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 " "(KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 " "(KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5", "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 " "(KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 " "(KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 " "(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24", "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 " "(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24" ] # 可被選用的代理IP PROXY_http = [ '153.180.102.104:80', '195.208.131.189:56055', ] PROXY_https = [ '120.83.49.90:9000', '95.189.112.214:35508', ] # 攔截所有未發生異常的請求 def process_request(self, request, spider): print(111) # 使用UA池進行請求的UA偽裝 # 為所有的請求添加 User-Agent request.headers["User-Agent"] = random.choice(self.user_agent_list) #隨機選擇user-agent,便於偽裝身份 return None # 如果這里返回的是response,那么就不再執行下面的方法了 # 攔截所有的響應 def process_response(self, request, response, spider): return response # 攔截產生異常的請求 def process_exception(self, request, exception, spider): print(222) # 使用代理池進行請求代理ip的設置, if request.url.split(":")[0] == "http": request.meta["proxy"] = random.choice(self.PROXY_http) else: request.meta["proxy"] = random.choice(self.PROXY_https) def spider_opened(self, spider): spider.logger.info('Spider opened: %s' % spider.name)
# settings.py
一定要選擇download_middlewares, 不能是spider_middlers
# Enable or disable spider middlewares # See https://doc.scrapy.org/en/latest/topics/spider-middleware.html #SPIDER_MIDDLEWARES = { # 'middlePro.middlewares.MiddleproSpiderMiddleware': 543, #} # Enable or disable downloader middlewares # See https://doc.scrapy.org/en/latest/topics/downloader-middleware.html DOWNLOADER_MIDDLEWARES = { 'middlePro.middlewares.MiddleproDownloaderMiddleware': 543, #添加該類 }
scrapy之selenium使用
''' 在scrapy中使用selenium的編碼流程: 1.在spider的構造方法中創建一個瀏覽器對象(作為當前spider的一個屬性) 2.重寫spider的一個方法closed(self,spider),在該方法中執行瀏覽器關閉的操作 3.在下載中間件的process_response方法中,通過spider參數獲取瀏覽器對象 4.在中間件的process_response中定制基於瀏覽器自動化的操作代碼(獲取動態加載出來的頁面源碼數據) 5.實例化一個響應對象,且將page_source返回的頁面源碼封裝到該對象中 6.返回該新的響應對象 '''
示例1:爬取網易新聞中的標題(包含一些動態加載的數據)
# wangyi.py
# -*- coding: utf-8 -*- import scrapy from selenium import webdriver class WangyiSpider(scrapy.Spider): name = 'wangyi' # allowed_domains = ['www.xxx.com'] start_urls = ['http://war.163.com/'] def __init__(self): # 包含動態加載數據時,使用selenium self.bro = webdriver.Chrome(executable_path=r"E:\data\PythonStudy\安裝軟件(下載)\爬蟲安裝包\chromedriver.exe") def parse(self, response): print(2222) div_list = response.xpath('//div[@class="data_row news_article clearfix "]') for div in div_list: title = div.xpath('.//div[@class="news_title"]/h3/a/text()').extract_first() print(title) def close(self,spider): self.bro.quit() print('關閉瀏覽器')
# middlewares.py
from scrapy.http import HtmlResponse
import time
class WangyiproDownloaderMiddleware(object): def process_request(self, request, spider): return None def process_response(self, request, response, spider): print('即將返回一個新的響應對象!!!') #如何獲取動態加載出來的數據 bro = spider.bro bro.get(url=request.url) sleep(3) #包含了動態加載出來的新聞數據 page_text = bro.page_source sleep(3) return HtmlResponse(url=spider.bro.current_url,body=page_text,encoding='utf-8',request=request) def process_exception(self, request, exception, spider): pass
# settings.py
#配置可通行的中間件,要寫在下載中間將中
DOWNLOADER_MIDDLEWARES = { 'wangyiPro.middlewares.WangyiproDownloaderMiddleware': 543, }
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36' # Obey robots.txt rules ROBOTSTXT_OBEY = False