九色国产,午夜在线视频,新黄色网址,九九色综合,天天做夜夜做久久做狠狠,天天躁夜夜躁狠狠躁2021a,久久不卡一区二区三区

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
爬蟲之Scrapy框架
  • 框架:具有很強的通用性,且封裝了一些通用實現(xiàn)方法的項目模板
  • scrapy(異步框架):
    • 高性能的網(wǎng)絡(luò)請求
    • 高性能的數(shù)據(jù)解析
    • 高性能的持久化存儲
    • 高性能的全站數(shù)據(jù)爬取
    • 高性能的深度爬取
    • 高性能的分布式

Scrapy環(huán)境安裝

IOS和Linux

  • pip install scrapy

windows

      a. pip3 install wheel      b. 下載twisted http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted            # Twisted?17.1.0?cp35?cp35m?win_amd64.whl; Python是3.5版本的就選擇cp35下載      c. 進入下載目錄,執(zhí)行 pip3 install Twisted?17.1.0?cp35?cp35m?win_amd64.whl          # 安裝失敗可能是這個文件的版本導(dǎo)致的,即使Python版本都是對的,可以重新下載一個32位的試試      # 還安裝失敗的話就下載其python版本的,總有一個能成功      d. pip3 install pywin32      e. pip3 install scrapy

安裝完成后,輸入``scrapy`測試一下,出現(xiàn)如下圖顯示,即安裝成功。

Scrapy的基本使用

創(chuàng)建工程

  • scrapy startprojct proNmame

    cd proNmame進入到工程目錄下執(zhí)行爬蟲文件

proName		# 工程名字	spiders		# 爬蟲包(文件夾)		__init__.py    __init__.py    items.py    middlewares.py    pipelines.py    settings.py		# 創(chuàng)建好的工程的配置文件scrapy.cfg		# scrapy的配置文件,不用修改

創(chuàng)建爬蟲文件

  • 創(chuàng)建爬蟲文件是py源文件
  • scrapy genspider spiderName www.xxx.com 網(wǎng)址后期可以修改
    • spiders包下創(chuàng)建一個py文件
# -*- coding: utf-8 -*-import scrapyclass FirstSpider(scrapy.Spider):	# scrapy.Spider所有爬蟲類的父類    # name表示的爬蟲文件的名稱,當(dāng)前爬蟲文件的唯一標(biāo)識    name = 'first'        # 允許的域名,通常會注釋掉    # allowed_domains = ['www.xx.com']        # 起始的url列表,最開始要爬的網(wǎng)址列表    # 作用:可以將內(nèi)部的列表元素進行g(shù)et請求的發(fā)送    start_urls = ['http://www.sougou.com/','www.baidu.com']    # 調(diào)用parse方法解析數(shù)據(jù),方法調(diào)用的次數(shù)由start_urls列表元素個數(shù)決定的    def parse(self, response):	# response表示一個響應(yīng)對象,        pass

基本配置

  • UA偽裝

  • robots協(xié)議的不遵從

    settings.py中將ROBOTSTXT_OBEY = True修改為False

  • 指定日志等級

    settings.py中添加LOG_LEVEL = 'ERROR'

執(zhí)行工程

  • scrapy crawl spiderName

  • 執(zhí)行工程是不展示日志文件

    scrapy crawl spiderName --nolog

    這種方式下程序報錯,不會展示;設(shè)置好日志等級后直接執(zhí)行工程即可。

數(shù)據(jù)解析

  • response.xpath('xpath表達(dá)式')

  • etree的不同之處:

    取文本/屬性:返回的是一個Selector對象,文本數(shù)據(jù)是存儲在該對象中

    • Selector對象[0].extract()返回字符串
    • Selector對象.extract_first()返回字符串
    • Selector對象.extract()返回列表

常用操作

  • 如果列表只有一個元素用Selector對象.extract_first(),返回字符串
  • 如果列表有多個元素Selector對象.extract(),返回列表,列表里裝的是字符串

spiderName.py文件

# -*- coding: utf-8 -*-import scrapyclass DuanziSpider(scrapy.Spider):    name = 'duanzi'    # allowed_domains = ['www.xx.com']    start_urls = ['https://duanziwang.com/']    def parse(self, response):        article_list = response.xpath('/html/body/section/div/div/main/article')  # 基于xpath表達(dá)式解析        for article in article_list:            title = article.xpath('./div[1]/h1/a/text()')[0]    # 返回一個Selector對象            # <Selector xpath='./div[1]/h1/a/text()' data='關(guān)于健康養(yǎng)生、延年益壽的生活諺語_段子網(wǎng)收錄最新段子'>            title = article.xpath('./div[1]/h1/a/text()')[0].extract()    # 返回字符串            # 關(guān)于健康養(yǎng)生、延年益壽的生活諺語_段子網(wǎng)收錄最新段子            title = article.xpath('./div[1]/h1/a/text()').extract_first()   # 返回字符串            # 關(guān)于健康養(yǎng)生、延年益壽的生活諺語_段子網(wǎng)收錄最新段子            title = article.xpath('./div[1]/h1/a/text()').extract()     # 返回列表            # ['關(guān)于健康養(yǎng)生、延年益壽的生活諺語_段子網(wǎng)收錄最新段子']            print(title)            break

持久化存儲

基于終端指令的持久化存儲

  • 只可以將parse方法的返回值存儲到指定后綴的文本文件中

    指定后綴:'json', 'jsonlines', 'jl', 'csv', 'xml', 'marshal', 'pickle',通常用csv

    指令scrapy crawl spiderName -o filePath

案例:將文本數(shù)據(jù)持久化存儲

# -*- coding: utf-8 -*-import scrapyclass DuanziSpider(scrapy.Spider):    name = 'duanzi'    # allowed_domains = ['www.xx.com']    start_urls = ['https://duanziwang.com/']    # 基于終端指令的持久化存儲    def parse(self, response):        article_list = response.xpath('/html/body/section/div/div/main/article')  # 基于xpath表達(dá)式解析        all_data = []        for article in article_list:            title = article.xpath('./div[1]/h1/a/text()').extract_first()            content = article.xpath('./div[2]/p//text()').extract()            content = ''.join(content)            dic = {                "title": title,                "content": content            }            all_data.append(dic)        return all_data# 終端指令# scrapy crawl spiderName -o duanzi.csv

基于管道的持久化存儲

? scrapy建議使用管道持久化存儲

實現(xiàn)流程

  • 數(shù)據(jù)解析(spiderName .py

  • 實例化item類型對象(items.py

    items.py的item類中定義相關(guān)的屬性

    fieldNmae = scrapy.Field()

  • 將解析的數(shù)據(jù)存儲封裝到item類型的對象中(spiderName .py

    item['fileName'] = value 給item對象的fieldNmae屬性賦值

  • 將item對象提交給(spiderName .py

    yield item 將item提交給優(yōu)先級最高的管道

  • 在管道中接收item,可以將item中存儲的數(shù)據(jù)進行任意形式的持久化存儲(pipelines.py

    process_item():負(fù)責(zé)接收item對象且對其進行持久化存儲

  • 在配置文件settings.py中開啟管道機制

    找到如下代碼,取消注釋

    ITEM_PIPELINES = {    # 300表示的是優(yōu)先級,數(shù)值越小,優(yōu)先級越高   'duanziPro.pipelines.DuanziproPipeline': 300,}

案例:將文本數(shù)據(jù)持久化存儲

按上述在settings.py找到管道代碼,取消注釋。

spiderName .py

# -*- coding: utf-8 -*-import scrapyfrom duanziPro.items import DuanziproItemclass DuanziSpider(scrapy.Spider):    name = 'duanzi'    # allowed_domains = ['www.xx.com']    start_urls = ['https://duanziwang.com/']    # 基于管道的持久化存儲    def parse(self, response):        article_list = response.xpath('/html/body/section/div/div/main/article')  # 基于xpath表達(dá)式解析        for article in article_list:            title = article.xpath('./div[1]/h1/a/text()').extract_first()            content = article.xpath('./div[2]/pre/code//text()').extract()            content = ''.join(content)            print(content)            # 實例化item對象            item = DuanziproItem()            # 通過中括號的形式訪問屬性給其賦值            item['title'] = title            item['content'] = content            # 向管道提交item            yield item

items.py

import scrapyclass DuanziproItem(scrapy.Item):    # define the fields for your item here like:    # name = scrapy.Field()    # 使用固有屬性定義了兩個屬性    # Field是一個萬能數(shù)據(jù)類型    title = scrapy.Field()    content = scrapy.Field()

pipelines.py

class DuanziproPipeline(object):    # 重寫父類的該方法:該方法只會在爬蟲開始的時候執(zhí)行一次    fp = None        # 打開文件    def open_spider(self, spider):        print('open spider')        self.fp = open('./duanzi.txt', 'w', encoding='utf-8')    # 關(guān)閉文件    def close_spider(self, spider):        print('close spider')        self.fp.close()    # 接收爬蟲文件返回item對象,process_item方法每調(diào)用一次可接收一個item對象    # item參數(shù):接收到的某一個item對象    def process_item(self, item, spider):        # 取值        title = item['title']        content = item['content']        self.fp.write(title + ":" + content + "\n")        return item

管道存儲細(xì)節(jié)處理

  • 管道文件中的管道類表示的是什么?

    一個管道類對應(yīng)的就是一種存儲形式(文本文件,數(shù)據(jù)庫)

    如果想要實現(xiàn)數(shù)據(jù)備份,則需要使用多個管道類(多種存儲形式:MySQL,Redis)

  • process_item中的 retutn item

    將item傳遞給下一個即將被執(zhí)行(按照配置文件中ITEM_PIPELINES得權(quán)重排序)的管道類

存儲到MySQL

pipelines.py中添加如下代碼

import pymysqlclass MysqlPipeline(object):    conn = None    cursor = None    def open_spider(self, spider):        self.conn = pymysql.Connect(host='127.0.0.1', port=3306, user='root', password='123', db='spider',                                    charset='utf8')    def process_item(self, item, spider):        # 取值        title = item['title']        content = item['content']        self.cursor = self.conn.cursor()        # sql語句        sql = 'insert into duanzi values ("%s","%s")' % (title, content)        try:            self.cursor.execute(sql)            self.conn.commit()        except Exception as e:            print(e)            self.conn.rollback()        return item    def close_spider(self, spider):        self.cursor.close()        self.conn.close()

settings.py中將MysqlPipeline類注冊到ITEM_PIPELINES中

ITEM_PIPELINES = {    # 300表示的是優(yōu)先級,數(shù)值越小,優(yōu)先級越高    'duanziPro.pipelines.DuanziproPipeline': 300,    'duanziPro.pipelines.MysqlPipeline': 301,}

存儲到Redis

  • 因為redis有的版本不支持存儲字典,下載2.10.6版本

    pip install redis==2.10.6

pipelines.py中添加如下代碼

from redis import Redisclass RedisPipeline(object):    conn = None    def open_spider(self, spider):        self.conn = Redis(host='127.0.0.1', port=6379, password='yourpassword')    def process_item(self, item, spider):        self.conn.lpush('duanziList', item)        # 報錯:因為redis有的版本不支持存儲字典,pip install redis==2.10.6

settings.py中將RedisPipeline類注冊到ITEM_PIPELINES中

ITEM_PIPELINES = {    # 300表示的是優(yōu)先級,數(shù)值越小,優(yōu)先級越高    'duanziPro.pipelines.DuanziproPipeline': 300,    'duanziPro.pipelines.RedisPipeline': 301,}

手動發(fā)送請求

  • 可以在start_urls這個列表中添加url,但是比較繁瑣

  • get請求發(fā)送

    yield scrapy.Request(url,callback)

    • url:指定好請求的url
    • callback:callback指定的回調(diào)函數(shù)一定會被執(zhí)行(數(shù)據(jù)解析)
  • post請求發(fā)送

    yield scrapy.FormRequest(url,callback,formdata)

    • formdata存放請求參數(shù),字典類型
  • 父類中start_requests請求發(fā)送的原理

# 簡單模擬父類的方法,主要看yielddef start_requests(self):    for url in self.start_urls:        # 發(fā)起get請求        yield scrapy.Request(url=url,callback=self.parse)        # 發(fā)起post請求,formdata存放請求參數(shù)        yield scrapy.FormRequest(url=url,callback=self.parse,formdata={})

代碼實現(xiàn)

  • 主要是在spiderName .py中使用遞歸方法,且明確遞歸結(jié)束的條件;

    使用父類yield實現(xiàn)全站爬取

# -*- coding: utf-8 -*-import scrapyfrom duanziPro.items import DuanziproItemclass DuanziSpider(scrapy.Spider):    name = 'duanzi'    # allowed_domains = ['www.xx.com']    start_urls = ['https://duanziwang.com/']        # 手動請求的發(fā)送,對其他頁碼的數(shù)據(jù)進行請求操作    # 定義通用url模板    url = "https://duanziwang.com/page/%d/"    pageNum = 2    def parse(self, response):        article_list = response.xpath('/html/body/section/div/div/main/article')  # 基于xpath表達(dá)式解析        all_data = []        for article in article_list:            title = article.xpath('./div[1]/h1/a/text()').extract_first()            content = article.xpath('./div[2]/pre/code//text()').extract()            content = ''.join(content)            # 實例化item對象            item = DuanziproItem()            # 通過中括號的形式訪問屬性給其賦值            item['title'] = title            item['content'] = content            # 向管道提交item            yield item        if self.pageNum < 5:            new_url = format(self.url%self.pageNum)            self.pageNum += 1            # 遞歸實現(xiàn)全站數(shù)據(jù)爬取,callback指定解析的方法            yield scrapy.Request(url=new_url, callback=self.parse)
  • pipelines.py中實現(xiàn)數(shù)據(jù)持久化存儲
class DuanziproPipeline(object):    # 重寫父類的該方法:該方法只會在爬蟲開始的時候執(zhí)行一次    fp = None    def open_spider(self, spider):        print('open spider')        self.fp = open('./duanzi.txt', 'w', encoding='utf-8')    # 關(guān)閉fp    def close_spider(self, spider):        print('close spider')        self.fp.close()    # 接收爬蟲文件返回item對象,process_item方法每調(diào)用一次可接收一個item對象    # item參數(shù):接收到的某一個item對象    def process_item(self, item, spider):        # 取值        title = item['title']        content = item['content']        self.fp.write(title + ":" + content + "\n")        # 將item轉(zhuǎn)交給下一個即將被執(zhí)行的管道類        return item
  • settings.py中開啟管道類
ITEM_PIPELINES = {    # 300表示的是優(yōu)先級,數(shù)值越小,優(yōu)先級越高    'duanziPro.pipelines.DuanziproPipeline': 300,}

yield在scrapy中的使用

  • 向管道中提交item對象

    yield item

  • 手動請求發(fā)送

    yield scrapy.Request(url,callback)

五大核心組件

  • 引擎(Scrapy Engine)

    處理整個系統(tǒng)的數(shù)據(jù)流,觸發(fā)事物(框架核心)。

  • 調(diào)度器(Scheduer)

    用來接收引擎發(fā)過來的請求,壓入隊列中,并在引擎再次請求的時候返回。

  • 下載器(Downloader)

    用于下載網(wǎng)頁內(nèi)容,并將網(wǎng)頁內(nèi)容返回給蜘蛛(Scrapy下載器是建立在twisted這個高效模型上的)。

  • 爬蟲(Spiders)

    爬蟲主要是干活的,用于從特定的網(wǎng)頁中提取自己需要的信息,即所謂的實體(item)。用戶也可以從中提取出鏈接,讓Scrapy繼續(xù)抓取下一個頁面

  • 管道(item Pipeline)

    負(fù)責(zé)處理爬蟲從網(wǎng)頁抽取的實體,主要的功能是持久化實體、驗證實體的有效性、清除不需要的信息。當(dāng)頁面被爬蟲解析后,將被發(fā)送到項目管道,并經(jīng)過幾個特定的次序處理數(shù)據(jù)。

五大核心組件的工作流程


當(dāng)執(zhí)行爬蟲文件時,5大核心組件就在工作了

首先執(zhí)行爬蟲文件spider,spider的作用是
(1)解析(2)發(fā)請求,原始的url存儲在于spider中
1:當(dāng)spider執(zhí)行的時候,首先對起始的url發(fā)送請求,將起始url封裝成請求對象
2:將請求對象傳遞給引擎
3:引擎將請求對象傳遞給調(diào)度器(內(nèi)部含有隊列和過濾器兩個機制),調(diào)度器將請求存儲在隊列(先進先出)中
4:調(diào)度器從隊列中調(diào)度出url的相應(yīng)對象再將請求傳遞給引擎
5:引擎將請求對象通過下載中間件發(fā)送給下載器
6:下載器拿到請求到互聯(lián)網(wǎng)上去下載
7:互聯(lián)網(wǎng)將下載好的數(shù)據(jù)封裝到響應(yīng)對象給到下載器
8:下載器將響應(yīng)對象通過下載中間件發(fā)送給引擎
9:引擎將封裝了數(shù)據(jù)的響應(yīng)對象回傳給spider類parse方法中的response對象
10:spider中的parse方法被調(diào)用,response就有了響應(yīng)值
11:在spider的parse方法中進行解析代碼的編寫;
(1)會解析出另外一批url,(2)會解析出相關(guān)的文本數(shù)據(jù)
12: 將解析拿到的數(shù)據(jù)封裝到item中
13:item將封裝的文本數(shù)據(jù)提交給引擎
14:引擎將數(shù)據(jù)提交給管道進行持久化存儲(一次完整的請求數(shù)據(jù))
15:如果parder方法中解析到的另外一批url想繼續(xù)提交可以繼續(xù)手動進行發(fā)請求
16:spider將這批請求對象封裝提交給引擎
17:引擎將這批請求對象發(fā)配給調(diào)度器
16:這批url通過調(diào)度器中過濾器過濾掉重復(fù)的url存儲在調(diào)度器的隊列中
17:調(diào)度器再將這批請求對象進行請求的調(diào)度發(fā)送給引擎

引擎作用:
1:處理流數(shù)據(jù) 2:觸發(fā)事物
引擎根據(jù)相互的數(shù)據(jù)流做判斷,根據(jù)拿到的流數(shù)據(jù)進行下一步組件中方法的調(diào)用

下載中間件: 位于引擎和下載器之間,可以攔截請求和響應(yīng)對象;攔截到請求和響應(yīng)對象后可以
篡改頁面內(nèi)容和請求和響應(yīng)頭信息。
爬蟲中間件:位于spider和引擎之間,也可以攔截請求和響應(yīng)對象,不常用。

請求傳參

  • 作用

    實現(xiàn)深度爬取。

  • 深度爬取

    爬取的數(shù)據(jù)不在同一張頁面中。

  • 在進行手動發(fā)送請求的時候,可以將一個meta字典傳遞給callback指定的回調(diào)函數(shù)

    • yield scrapy.Request(url,callback,meta={})

    • 在回調(diào)函數(shù)中接收meta

      response.meta['key'] 將meta字典中key對應(yīng)的value值取出

案例:電影名字和簡介爬取

spiderName.py

# -*- coding: utf-8 -*-import scrapyfrom moviePro.items import MovieproItemclass MovieSpider(scrapy.Spider):    name = 'movie'    # allowed_domains = ['www.xxx.com']    start_urls = ['https://www.4567kan.com/frim/index1.html']    # 通用url    url = 'https://www.4567kan.com/frim/index1-%d.html'    pagNum = 2    # 解析首頁的數(shù)據(jù),爬取電影名稱和簡介    def parse(self, response):        li_list = response.xpath('/html/body/div[1]/div/div/div/div[2]/ul/li')        for li in li_list:            mv_name = li.xpath('./div/a/@title').extract_first()            item = MovieproItem()            item['name'] = mv_name            detail_url = "https://www.4567kan.com/" + li.xpath('./div/a/@href').extract_first()            yield scrapy.Request(url=detail_url, callback=self.parse_detail, meta={'item': item})            # meta 就是一個字典,可以將字典傳給callback指定的回調(diào)函數(shù),實現(xiàn)請求傳參        # 全站信息的爬取,測試前4頁        if self.pagNum < 5:            new_url = format(self.url % self.pagNum)            self.pagNum += 1            yield scrapy.Request(url=new_url,callback=self.parse)    # 自定義解析方法,解析詳情頁電影簡介    def parse_detail(self, response):        desc = response.xpath('/html/body/div[1]/div/div/div/div[2]/p[5]/span[3]//text()').extract_first()        # 接收請求傳參傳遞過來的item        item = response.meta['item']        item['desc'] = desc        yield item

中間件

爬蟲中間件

位于spider和引擎之間,也可以攔截請求和響應(yīng)對象,不常用。

下載中間件(推薦)

位于引擎和下載器之間,可以攔截請求和響應(yīng)對象;攔截到請求和響應(yīng)對象后可以
篡改頁面內(nèi)容和請求和響應(yīng)頭信息。

作用:攔截所有請求和響應(yīng)

為什么要攔截請求?

  • UA偽裝(篡改請求頭信息)

    process_request()方法中,

    request.headers['User-Agent'] ="請求頭信息"

  • 設(shè)置代理

    process_exception()方法中,

    request.meta['proxy'] = 'https://ip:prot'

    return request

工程項目中middlewares.py就是中間件。

from scrapy import signalsimport random# UA池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"]PROXY_HTTP = ["ip:port"]PROXY_HTTPS = ["ip:port"]# 下載中間件class MiddleproDownloaderMiddleware(object):    def process_request(self, request, spider):        """        攔截正常請求        :param request: 攔截到的正常請求        :param spider: 爬蟲類實例化的對象        :return:         """        # UA偽裝        request.headers['User-Agent'] = random.choice(user_agent_list)        return None    def process_response(self, request, response, spider):        """        process_response函數(shù):攔截所有的響應(yīng)        :param request: 響應(yīng)對應(yīng)的請求        :param response: 攔截到的響應(yīng)        :param spider: 爬蟲類實例化的對象        :return: 返回處理后的響應(yīng)        """        return response    def process_exception(self, request, exception, spider):        """        攔截發(fā)生異常的請求;對異常請求進行修正,讓其變成正常請求        :param request: 攔截到的發(fā)生異常的請求對象        :param exception: 攔截到的異常信息        :param spider: 爬蟲類實例化的對象        :return: 將修正后的請求對象進行重新發(fā)送        """        # 代理操作        if request.url.split(":")[0] == 'http':            request.meta['proxy'] = "http:{}".format(random.choice(PROXY_HTTP))        else:            request.meta['proxy'] = "https:{}".format(random.choice(PROXY_HTTPS))        return request  # 將修正后的請求對象進行重新發(fā)送

為什么要攔截響應(yīng)?

  • 篡改響應(yīng)數(shù)據(jù)

案例:網(wǎng)易新聞數(shù)據(jù)爬取

  • 實現(xiàn)流程:

    1,解析出5個板塊對應(yīng)的url

    2,對5個板塊的url發(fā)起請求

    3,獲取板塊的頁面源碼數(shù)據(jù)

    ? 問題:數(shù)據(jù)為動態(tài)加載,源碼數(shù)據(jù)中沒有新聞標(biāo)題和詳情頁的url

    ? 解決:將響應(yīng)數(shù)據(jù)進行篡改,改成包含動態(tài)加載的數(shù)據(jù)

  • selenium幫助我們捕獲到包含了動態(tài)加載的響應(yīng)數(shù)據(jù)

    selenium在scrapy中的使用

    • 實例化一個瀏覽器對象(爬蟲文件中)
    • 在中間件process_response中進行selenium后續(xù)的操作
    • 在爬蟲文件的爬蟲類沖重寫一個closed(self,spider),關(guān)閉瀏覽器

在settings.py基本設(shè)置一下,打開下載中間件

spiderNmae.py

# -*- coding: utf-8 -*-import scrapyfrom selenium import webdriverfrom wangyiPro.items import WangyiproItemclass WangyiSpider(scrapy.Spider):    name = 'wangyi'    # allowed_domains = ['www.xx.com']    start_urls = ['https://news.163.com/']    # 5個板塊頁面的url    model_urls = []    # 實例化瀏覽器對象    bro = webdriver.Chrome(executable_path=r'D:\Reptile\jupyter\onceagain\爬蟲\Scrap框架\chromedriver.exe')    # 數(shù)據(jù)解析,解析5個板塊對應(yīng)頁面url    def parse(self, response):        li_list = response.xpath('//*[@id="index2016_wrap"]/div[1]/div[2]/div[2]/div[2]/div[2]/div/ul/li')        index = [3, 4, 6, 7, 8]        for i in index:            model_url = li_list[i].xpath('./a/@href').extract_first()            self.model_urls.append(model_url)        # 對板塊url發(fā)請求,捕獲每個頁面的源碼數(shù)據(jù)        for url in self.model_urls:            yield scrapy.Request(url=url, callback=self.parse_model)    # 解析標(biāo)題和新聞詳情頁的url    def parse_model(self, response):        div_list = response.xpath('/html/body/div[1]/div[3]/div[4]/div[1]/div/div/ul/li/div/div')        for div in div_list:            title = div.xpath('./a/img/@alt').extract_first()            new_detail_url = div.xpath('./a/@href').extract_first()            if new_detail_url:                item = WangyiproItem()                item['title'] = title                # 對新聞的詳情頁發(fā)請求,解析出新聞的內(nèi)容                yield scrapy.Request(url=new_detail_url, callback=self.parse_new_detail, meta={'item': item})    # 解析新聞內(nèi)容    def parse_new_detail(self, response):        item = response.meta['item']        content = response.xpath('//*[@id="endText"]//text()').extract()        content = ''.join(content)        # print(content)        item['content'] = content        yield item    # 整個程序結(jié)束時調(diào)用一次:父類的方法    def closed(self, spider):        self.bro.quit()

middlewares.py

from time import sleepclass WangyiproDownloaderMiddleware(object):    # 攔截響應(yīng),篡改指定響應(yīng)對象的響應(yīng)數(shù)據(jù)    def process_response(self, request, response, spider):        # 獲取5個板塊對應(yīng)的url        model_urls = spider.model_urls        bro = spider.bro        if request.url in model_urls:  # 成立之后定位到的response就是某一個板塊對應(yīng)的response            # 指定響應(yīng)數(shù)據(jù)的篡改            # 參數(shù)body就是響應(yīng)數(shù)據(jù)            bro.get(request.url)            sleep(1)            page_text = bro.page_source  # 作為新的響應(yīng)數(shù)據(jù),包含動態(tài)加載數(shù)據(jù)源            return HtmlResponse(url=request.url, body=page_text, encoding='utf-8', request=request)        else:            return response

items.py

import scrapyclass WangyiproItem(scrapy.Item):    title = scrapy.Field()    content = scrapy.Field()

Scrapy爬大文本數(shù)據(jù)

大文本數(shù)據(jù)就是量級大的二進制數(shù)據(jù),如圖片,壓縮包,音頻,視頻...

  • 爬蟲文件中將二進制資源的url進行爬取和解析,將其存儲到item中向管道提交

  • 在管道文件中指定對應(yīng)的管道類

    父類:from scrapy.pipelines.images import ImagesPipeline

    配置文件中進行如下操作

    # 自動創(chuàng)建一個指定的文件夾IMAGES_STORE = './imgLib'

案例:校花圖片的爬取

自定義一個關(guān)于ImagesPipeline該父類的管道類,在pipelines.py中重寫如下三個方法

from scrapy.pipelines.images import ImagesPipelineimport scrapyclass ImgproPipeline(ImagesPipeline):    # 發(fā)起請求    def get_media_requests(self, item, info):        imgSrc = item['imgSrc']        # 請求傳參,將meta字典傳遞給了file_path這個方法        yield scrapy.Request(url=imgSrc, meta={'item': item})    # 定制get_media_request請求到數(shù)據(jù)持久化存儲的路徑(文件夾路徑+文件名稱)    def file_path(self, request, response=None, info=None):        # 通過request.meta接收請求傳參傳遞過來的meta字典        imgName = request.meta['item']['imgName']        return imgName    	# 如果配置文件中沒指定文件夾        # return '文件夾/%s.jpg' % (image_guid)    def item_completed(self, results, item, info):        return item

CrawlSpider

全站數(shù)據(jù)爬取

  • CrawlSpider就是Spider的一個子類

  • 創(chuàng)建一個基于CrawlSpider爬蟲文件

    scrapy genspider -t crawl spiderName www.xxx.com

  • LinkExtractor(allow=r'正則表達(dá)式'):鏈接提取器

    • 作用:可以根據(jù)指定的指定的規(guī)則(allow)進行鏈接提取
  • Rule(link,callback,follow=True):規(guī)則解析器

    link:鏈接提取

    callback:回調(diào)函數(shù),字符串反射調(diào)用函數(shù),解析數(shù)據(jù)

    follow=True:將鏈接提取器 繼續(xù)作用到 鏈接提取器提取到的鏈接的對應(yīng)頁面中

    • 作用:

      1.將鏈接提取器提取到的鏈接進行請求發(fā)送(get)請求發(fā)送

      2.請求到的數(shù)據(jù)根據(jù)指定的規(guī)則進行數(shù)據(jù)解析

  • 深度爬取

    手動發(fā)送請求解析數(shù)據(jù),請求傳參和LinkExtractor,Rule一起使用實現(xiàn)深度爬取

Rule(LinkExtractor(allow=r'正則表達(dá)式提取指定url'),callback='函數(shù)名',follow=True(提取全站頁碼url)/Flase(提取當(dāng)前葉頁碼url))

使用鏈接提取器提取詳情頁的url實現(xiàn)深度爬取

案例:陽光問政

spiderName.py中代碼

  • 問題:實例化在持久化存儲后無法實現(xiàn)數(shù)據(jù)一一對應(yīng)的匯總
import scrapyfrom scrapy.linkextractors import LinkExtractorfrom scrapy.spiders import CrawlSpider, Rule# 實例化了兩個item對象from sunPro.items import TitleItem, ContentItemclass SunSpider(CrawlSpider):    name = 'sun'    # allowed_domains = ['www.xxx.com']    start_urls = ['http://wz.sun0769.com/political/index/politicsNewest?id=1&page=1']    # 根據(jù)正則提取全站頁碼    link = LinkExtractor(allow=r'id=1&page=\d+')    # 提取標(biāo)題對應(yīng)的詳情頁鏈接    link_detail = LinkExtractor(allow=r'politics/index\?id=\d+')    rules = (        Rule(link, callback='parse_item', follow=True),        Rule(link_detail, callback='parse_detail', follow=False),    )    def parse_item(self, response):        li_list = response.xpath('/html/body/div[2]/div[3]/ul[2]/li')        for li in li_list:            title = li.xpath('./span[3]/a/text()').extract_first()            detail_url =            item = TitleItem()            item['title'] = title            yield item    def parse_detail(self, response):        content = response.xpath('/html/body/div[3]/div[2]/div[2]/div[2]/pre/text()').extract_first()        item = ContentItem()        item['content'] = content        yield item
  • 解決上述問題

    手動發(fā)送請求解析詳情頁的內(nèi)容,請求傳參,傳給一個item

import scrapyfrom scrapy.linkextractors import LinkExtractorfrom scrapy.spiders import CrawlSpider, Rulefrom sunPro.items import SunproItemclass SunSpider(CrawlSpider):    name = 'sun'    # allowed_domains = ['www.xxx.com']    start_urls = ['http://wz.sun0769.com/political/index/politicsNewest?id=1&page=1']    link = LinkExtractor(allow=r'id=1&page=\d+')    rules = (        Rule(link, callback='parse_item', follow=False),    )    def parse_item(self, response):        li_list = response.xpath('/html/body/div[2]/div[3]/ul[2]/li')        for li in li_list:            title = li.xpath('./span[3]/a/text()').extract_first()            detail_url = "http://wz.sun0769.com/" + li.xpath('./span[3]/a/@href').extract_first()            item = SunproItem()            item['title'] = title            yield scrapy.Request(url=detail_url, callback=self.parse_detail, meta={'item': item})    def parse_detail(self, response):        content = response.xpath('/html/body/div[3]/div[2]/div[2]/div[2]/pre/text()').extract_first()        item = response.meta['item']        item['content'] = content        yield item

分布式

實際應(yīng)用中很少,一般都是面試時問道,主要是文原理。

  • 概念:搭建一個分布式機群,共同執(zhí)行一組代碼,聯(lián)合對同一個資源的數(shù)據(jù)進行分布且聯(lián)合爬取

  • 實現(xiàn)方式:

    簡稱:scrapy + redis

    全稱:Scrapy框架 + scrapy-redis組件

  • 原生的scrapy框架無法實現(xiàn)分布式

    原生scrapy的調(diào)度器和管道無法共享

  • scrapy-redis組件的作用

    可以給原生的scrapy框架,提供可以被共享的管道和調(diào)度器

  • 環(huán)境安裝

    pip install scrapy-redis

  • 實現(xiàn)流程

    修改爬蟲文件中爬蟲類對應(yīng)的操作

    • 導(dǎo)包:from scrapy_redis.spiders import RedisCrawlSpider

      CrawlSpider     導(dǎo)入 RedisCrawlSpiderSpider			導(dǎo)入 RedisSpider
    • 爬蟲類的父類修改成RedisCrawlSpider

    • 將start_urls刪除,添加一個redis_key='可以被共享調(diào)度器隊列的名稱'

    • 進行常規(guī)的請求和解析和向管道提交item操作即可

    settings.py進行配置

      • 配置管道
      ITEM_PIPELINES = {    # scrapy組件中有管道,是基于redis的,所以目前只能用redis存儲    'scrapy_redis.pipelines.RedisPipeline':400,}
      • 調(diào)度器的配置
      # 增加了一個去重容器類的配置,作用使用Redis的set集合來存儲請求的指紋數(shù)據(jù),從而實現(xiàn)請求去重的持久化DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"# 使用scrapy-redis組件自己的調(diào)度器SCHEDULER = "scraoy_redis.scheduler.Scheduler"# 配置調(diào)度器是否要持久化,也就是爬蟲結(jié)束,是否清空Redis中請求對列和去重的set,true表示持久化存儲,不清空SCHEDULER_PERSIST = True
      • 對Redis進行配置
      REDIS_HOST = 'redis服務(wù)的ip地址'REDIS_PORT = 6379

    對redis配置文件進行修改

    • 56行:# bind 127.0.0.1
    • 75行:protected-mode no

    啟動Redis服務(wù)和客戶端

    • 攜帶配置文件啟動redis,在redis安裝目錄下運行cmd

      redis-server.exe redis.windows.conf

    • 啟動客戶端

      redis-cli

    啟動程序

    • 在終端中進入到爬蟲文件對應(yīng)的目錄中

      scrapy runspider spiderName.py

    • 向調(diào)度器的隊列中扔入一個起始的url

      redisl-cli

      lpush redis_key的屬性值(被共享的調(diào)度器隊列名稱) 起始的網(wǎng)址

示例代碼

# -*- coding: utf-8 -*-import scrapyfrom scrapy.linkextractors import LinkExtractorfrom scrapy.spiders import CrawlSpider, Rulefrom scrapy_redis.spiders import RedisCrawlSpiderfrom fbsPro.items import FbsproItemclass FbsSpider(RedisCrawlSpider):    name = 'fbs'    # allowed_domains = ['www.xxx.com']    # start_urls = ['http://www.xxx.com/']    redis_key = 'sunQueue'  # 可被共享的調(diào)度器隊列的名稱    rules = (        Rule(LinkExtractor(allow=r'id=1&page=\d+'), callback='parse_item', follow=True),    )    def parse_item(self, response):        li_list = response.xpath('/html/body/div[2]/div[3]/ul[2]/li')        for li in li_list:            title = li.xpath('./span[3]/a/text()').extract_first()            item = FbsproItem()            item['title'] = title            yield item

增量式

  • 監(jiān)測網(wǎng)站數(shù)據(jù)更新情況,以便于爬取到最新更新的網(wǎng)站

  • 核心:去重

  • 記錄儀:

    特性:永久性存儲(redis中的set)

    爬取過的數(shù)據(jù)對應(yīng)的url

    • 可以以明文的形式存儲(url數(shù)據(jù)長度較短)

    • 記錄的數(shù)據(jù)對其生成一個數(shù)據(jù)指紋(url數(shù)據(jù)長度比較長)

      數(shù)據(jù)指紋就是該組數(shù)據(jù)的唯一標(biāo)識

示例代碼

# -*- coding: utf-8 -*-import scrapyfrom scrapy.linkextractors import LinkExtractorfrom scrapy.spiders import CrawlSpider, Rulefrom redis import Redisfrom zlsPro.items import ZlsproItemclass ZlsSpider(CrawlSpider):    name = 'zls'    # allowed_domains = ['www.xxx.com']    start_urls = ['http://wz.sun0769.com/political/index/politicsNewest?id=1&page=1']    # 創(chuàng)建redis的鏈接對象    conn = Redis(host="127.0.0.1", port=6379, password='redis的密碼,沒有就不寫')    rules = (        Rule(LinkExtractor(allow=r'id=1&page=\d+'), callback='parse_item', follow=False),    )    def parse_item(self, response):        # 解析出標(biāo)題和詳情頁的url(詳情頁的url需要存儲到記錄表中)        li_list = response.xpath('/html/body/div[2]/div[3]/ul[2]/li')        for li in li_list:            title = li.xpath('./span[3]/a/text()').extract_first()            detail_url = "http://wz.sun0769.com/" + li.xpath('./span[3]/a/@href').extract_first()            item = ZlsproItem()            item['title'] = title            # 將進行請求發(fā)送的詳情頁的url去記錄表中進行查看            ex = self.conn.sadd('urls', detail_url)            if ex == 1:                print('數(shù)據(jù)已更新,可爬取')                yield scrapy.Request(url=detail_url, callback=self.parse_deatil, meta={'item': item})            else:                print('數(shù)據(jù)未更新,不可爬')    def parse_deatil(self, response):        item = response.meta['item']        content = response.xpath('/html/body/div[3]/div[2]/div[2]/div[2]/pre/text()').extract_first()        item['content'] = content        yield item
代碼改變世界,腳踏實地,python、Golang。
本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
python爬蟲入門,10分鐘就夠了,這可能是我見過最簡單的基礎(chǔ)教學(xué)
Python爬蟲框架Scrapy實例(三)數(shù)據(jù)存儲到MongoDB
【爬蟲學(xué)習(xí)筆記day45】5.3. (scrapy案例三)新浪網(wǎng)分類資訊爬蟲
Python爬蟲教程使用Scrapy框架爬取小說代碼示例
【下篇】Python下用Scrapy和MongoDB構(gòu)建爬蟲系統(tǒng)
從原理到實戰(zhàn),一份詳實的 Scrapy 爬蟲教程,值得收藏
更多類似文章 >>
生活服務(wù)
熱點新聞
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服