爬蟲進階版


1. 移動端數據抓取

fillder進行一個基本的配置:tools->options->https->Decry..
fillder進行一個基本的配置:tools->options->connection->allow remote 
http://fillder所在pc機的ip+port/:訪問到一張提供了證書下載功能的頁面
fiddler所在的機器和手機在同一網段下:在手機瀏覽器中訪問http://fillder所在pc機的ip:8888/
獲取子頁面進行證書的下載和安裝(證書信任的操作) 
配置你的手機的代理:將手機的代理配置成fiddler所對應pc機的ip和fillder自己的端口
就可以讓fiddler捕獲手機發起的http和https的請求

2. scrapy框架

框架就是一個集成了各種功能且具有很強通用性(可以被應用在各種不同的需求中)的一個項目模板.

scrapy集成了哪些功能:

高性能的數據解析操作,持久化存儲操作,高性能的數據下載的操作.....

3.環境的安裝:

pip3 install wheel

下載twisted http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted

進入下載目錄,執行 
pip3 install Twisted-20.3.0-cp37-cp37m-win_amd64.whl

pip3 install pywin32

4 scrapy的基本使用

創建一個工程:scrapy startproject zbb
必須在spiders這個目錄下創建一個爬蟲文件
cd zbb
scrapy genspider first www.baidu.com
import scrapy

class FirstSpider(scrapy.Spider):
    # 爬蟲文件的名稱:爬蟲文件的唯一標識(在spiders子目錄下是可以創建多個爬蟲文件)
    name = 'first'
    # 允許的域名
    # allowed_domains = ['www.baidu.com']
    # 起始的url列表:列表中存放的url會被scrapy自動的進行請求發送
    start_urls = ['https://www.baidu.com/', 'https://www.sogou.com/']

    # 用作於數據解析:將start_urls列表中對應的url請求成功后的響應數據進行解析
    def parse(self, response):
        pass

執行工程

scrapy crawl first

settings.py

#不遵從robots協議
ROBOTSTXT_OBEY = False  
#進行UA偽裝
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36'
#進行日志等級設定
#scrapy crawl first  --nolog
LOG_LEVEL = 'ERROR'

5.持久化存儲

1.基於終端指令:

特性:只可以將parse方法的返回值存儲到本地的磁盤文件中
指令:scrapy crawl first -o quibai.csv
import scrapy


class FirstSpider(scrapy.Spider):
   name = 'first'
   start_urls = ['https://www.qiushibaike.com/text/']

   def parse(self, response):
       div_list = response.xpath('//*[@id="content"]/div/div[2]/div')
       all_data = []
       for div in div_list:
           # xpath返回的列表元素一定是Selector對象
           # 最終要解析的數據儲存在改對象中
           # extract()將data參數取值
           # author = div.xpath("./div[1]/a[2]/h2/text()")[0].extract()
           author = div.xpath("./div[1]/a[2]/h2/text()").extract_first()
           # 直接調用是將extract作用到每個列表元素中
           con = div.xpath('./a[1]/div/span//text()').extract()
           # 將列表轉換為字符轉
           con = ''.join(con)
           dic = {
               'author': author,
               'content': con
           }
           all_data.append(dic)
       return all_data

2.基於管道:

1.數據解析
2.在item類中定義相關的屬性
3.將解析的數據存儲或者封裝到一個item類型的對象(items文件中對應類的對象)
4.向管道提交item
5.在管道文件的process_item方法中接收item進行持久化存儲
6.在配置文件中開啟管道
ITEM_PIPELINES = {
   'zbb.pipelines.ZbbPipeline': 300, #300表示優先值
}

item

import scrapy


class ZbbItem(scrapy.Item):
    # define the fields for your item here like:
    author = scrapy.Field()
    con = scrapy.Field()

first.py

import scrapy
from zbb.items import ZbbItem
class FirstSpider(scrapy.Spider):
    name = 'first'
    start_urls = ['https://www.qiushibaike.com/text/']

    def parse(self, response):
        div_list = response.xpath('//*[@id="content"]/div/div[2]/div')
        all_data = []
        for div in div_list:
            author = div.xpath("./div[1]/a[2]/h2/text()")[0].extract()
            con = div.xpath('./a[1]/div/span//text()').extract()
            con = ''.join(con)
            #將解析的數據儲存到item對象中
            item = ZbbItem()
            item['author'] =author
            item['con'] =con
            #將item提交到管道
            yield item

pipelines.py

class ZbbPipeline:
    fp = None
    def open_spider(self, spider):
        print('開始爬蟲......')
        self.fp = open('qiushibaike.txt', 'w', encoding='utf-8')

    # 使用來接收爬蟲文件提交過來的item,然后將其進行任意形式的持久化存儲
    # 參數item:就是接收到的item對象
    # 該方法每接收一個item就會調用一次
    def process_item(self, item, spider):
        author = item['author']
        con = item['con']
        self.fp.write(author + ':' + con + '\n')
        return item  # item是返回給了下一個即將被執行的管道類

    def close_spider(self, spider):
        print('結束爬蟲!')
        self.fp.close()

3.將同一份數據持久化到不同的平台中

  • 分析:

    • 1.管道文件中的一個管道類負責數據的一種形式的持久化存儲
    • 2.爬蟲文件向管道提交的item只會提交給優先級最高的那一個管道類
    • 3.在管道類的process_item中的return item表示的是將當前管道接收的item返回/提交給
      下一個即將被執行的管道類

    setting配置

ITEM_PIPELINES = {
    'zbb.pipelines.ZbbPipeline': 300,  # 300表示優先值
    'zbb.pipelines.MysqlPL': 301,  # 300表示優先值 越小越好
    'zbb.pipelines.RedisPL': 302,

}

pipelines

import pymysql
from redis import Redis

class ZbbPipeline:
    fp = None

    def open_spider(self, spider):
        print('開始爬蟲......')
        self.fp = open('qiushibaike.txt', 'w', encoding='utf-8')

    # 使用來接收爬蟲文件提交過來的item,然后將其進行任意形式的持久化存儲
    # 參數item:就是接收到的item對象
    # 該方法每接收一個item就會調用一次
    def process_item(self, item, spider):
        author = item['author']
        con = item['con']
        self.fp.write(author + ':' + con + '\n')
        return item  # item是返回給了下一個即將被執行的管道類

    def close_spider(self, spider):
        print('結束爬蟲!')
        self.fp.close()


class MysqlPL:
    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')
        print(self.conn)

    def process_item(self, item, spider):
        author = item['author']
        con = item['con']
        sql = 'insert into qiubai values ("%s","%s")'%(author, con)
        self.cursor = self.conn.cursor()
        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()

class RedisPL:
    conn = None

    def open_spider(self, spider):
        self.conn = Redis(host='127.0.0.1', port=6379)
        print(self.conn)

    def process_item(self, item, spider):
        self.conn.lpush('all_data', item)
        # 注意:如果將字典寫入redis報錯:pip install -U redis==2.10.6

6.在scrapy中手動請求發送(GET)

  • 使用場景:爬取多個頁碼對應的頁面源碼數據
  • yield scrapy.Request(url,callback)
import scrapy
from zbb.items import ZbbItem


class FirstSpider(scrapy.Spider):
    name = 'first'
    start_urls = ['https://www.qiushibaike.com/text/']
    # 將多個頁碼對應的頁面數據進行爬取和解析的操作
    url = 'https://www.qiushibaike.com/text/page/%d/'  # 通用的url模板
    pageNum = 1

    def parse(self, response):
        div_list = response.xpath('//*[@id="content"]/div/div[2]/div')
        all_data = []
        for div in div_list:
            author = div.xpath("./div[1]/a[2]/h2/text()")[0].extract()
            con = div.xpath('./a[1]/div/span//text()').extract()
            con = ''.join(con)
            # 將解析的數據儲存到item對象中
            item = ZbbItem()
            item['author'] = author
            item['con'] = con
            # 將item提交到管道
            yield item

        if self.pageNum <= 5:
            self.pageNum += 1
            new_url = format(self.url%self.pageNum)
            # 手動請求(get)的發送
            yield scrapy.Request(new_url, callback=self.parse)

7.在scrapy中手請求發送(POST)

一般不用除非瘋了 很麻煩

data = { #post請求的請求參數
    'kw':'aaa'
}
yield scrapy.FormRequest(url,formdata=data,callback)

8.scrapy五大核心組件的工作流程:

引擎(Scrapy)
用來處理整個系統的數據流處理, 觸發事務(框架核心)
調度器(Scheduler)
用來接受引擎發過來的請求, 壓入隊列中, 並在引擎再次請求的時候返回. 可以想像成一個URL(抓取網頁的網址或者說是鏈接)的優先隊列, 由它來決定下一個要抓取的網址是什么, 同時去除重復的網址
下載器(Downloader)
用於下載網頁內容, 並將網頁內容返回給蜘蛛(Scrapy下載器是建立在twisted這個高效的異步模型上的)
爬蟲(Spiders)
爬蟲是主要干活的, 用於從特定的網頁中提取自己需要的信息, 即所謂的實體(Item)。用戶也可以從中提取出鏈接,讓Scrapy繼續抓取下一個頁面
項目管道(Pipeline)
負責處理爬蟲從網頁中抽取的實體,主要的功能是持久化實體、驗證實體的有效性、清除不需要的信息。當頁面被爬蟲解析后,將被發送到項目管道,並經過幾個特定的次序處理數據。

9.基於scrapy進行圖片數據的爬取

在爬蟲文件中只需要解析提取出圖片地址,然后將地址提交給管道
配置文件中:IMAGES_STORE = './imgsLib'
    在管道文件中進行管道類的制定:
    1.from scrapy.pipelines.images import  ImagesPipeline
    2.將管道類的父類修改成ImagesPipeline
    3.重寫父類的三個方法:

1.爬取校花網圖片

第一步: 創建一個項目

scrapy startproject zxy

第二步: 創建一個爬蟲文件

scrapy genspider img www.baidu.com

第三步:配置Stettings

#UA偽裝
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36'
#不遵循reboot協議
ROBOTSTXT_OBEY = False
#顯示日志
LOG_LEVEL = 'ERROR'
#圖片地址
IMAGES_STORE = './imgsLib'
#開啟管道
ITEM_PIPELINES = {
   'zxy.pipelines.ZxyPipeline': 300,
}

img.py

import scrapy
from zxy.items import ZxyItem

class ImgSpider(scrapy.Spider):
    name = 'img'
    # allowed_domains = ['www.baidu.com']
    start_urls = ['http://www.521609.com/daxuemeinv/']
    url = 'http://www.521609.com/daxuemeinv/list8%d.html'
    pageNum = 1
    def parse(self, response):
        li_list = response.xpath('//*[@id="content"]/div[2]/div[2]/ul/li')
        for li in li_list:
            img_src = "http://www.521609.com/" + li.xpath("./a[1]/img/@src").extract_first()
            item = ZxyItem()
            item['src'] = img_src
            yield item
        if self.pageNum < 3:
            self.pageNum += 1
            new_url = format(self.url%self.pageNum)
            # 手動請求(get)的發送
            yield scrapy.Request(new_url, callback=self.parse)


item.py

import scrapy


class ZxyItem(scrapy.Item):
    # define the fields for your item here like:
    src = scrapy.Field()

管道

from scrapy.pipelines.images import ImagesPipeline
import scrapy
# class ZxyPipeline:
#     def process_item(self, item, spider):
#         return item
class ZxyPipeline(ImagesPipeline):
    #對某一個媒體資源進行請求發送
    #item就是接受到spider發送過來的item
    def get_media_requests(self, item, info):
        yield scrapy.Request(item['src'])
    #制定媒體數據存儲的名稱
    def file_path(self, request, response=None, info=None):
        name = request.url.split('/')[-1]
        print("go" + name)
        return name
    #完成之后將item給下一個管道類
    # def item_completed(self, results, item, info):
    #     return item

10.scrapy爬取數據的效率

只需要將如下五個步驟配置在配置文件中即可

增加並發:
默認scrapy開啟的並發線程為32個,可以適當進行增加。在settings配置文件中修改CONCURRENT_REQUESTS = 100值為100,並發設置成了為100。

降低日志級別
在運行scrapy時,會有大量日志信息的輸出,為了減少CPU的使用率。可以設置log輸出信息為INFO或者ERROR即可。在配置文件中編寫:LOG_LEVEL = ‘INFO’

禁止cookie
如果不是真的需要cookie,則在scrapy爬取數據時可以禁止cookie從而減少CPU的使用率,提升爬取效率。在配置文件中編寫:COOKIES_ENABLED = False

禁止重試:
對失敗的HTTP進行重新請求(重試)會減慢爬取速度,因此可以禁止重試。在配置文件中編寫:RETRY_ENABLED = False

減少下載超時:
如果對一個非常慢的鏈接進行爬取,減少下載超時可以能讓卡住的鏈接快速被放棄,從而提升效率。在配置文件中進行編寫:DOWNLOAD_TIMEOUT = 10 超時時間為10s

11.請求傳參(實現深度爬取)

實現深度爬取:爬取多個層級對應的頁面數據
使用場景:爬取的數據沒有在同一張頁面中(如前面爬取的boos直聘)

#在手動請求的時候傳遞item:yield scrapy.Request(url,callback,meta={'item':item})
#將meta這個字典傳遞給callback
#在callback中接收meta:item = response.meta['item']

1.爬取www.4567kan.com

第一步: 創建一個項目

scrapy startproject mv

第二步: 創建一個爬蟲文件

scrapy genspider movie www.baidu.com

第三步:配置Stettings

#UA偽裝
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36'
#不遵循reboot協議
ROBOTSTXT_OBEY = False
#顯示日志
LOG_LEVEL = 'ERROR'
#開啟管道
ITEM_PIPELINES = {
   'zxy.pipelines.ZxyPipeline': 300,
}

movie.py

import scrapy
from mv.items import MvItem


class MovieSpider(scrapy.Spider):
    name = 'movie'
    start_urls = ['https://www.4567kan.com/index.php/vod/show/class/%E5%8A%A8%E4%BD%9C/id/1/page/1.html']

    url = 'https://www.4567kan.com/index.php/vod/show/class/%E5%8A%A8%E4%BD%9C/id/1/page/%d.html'
    pageNum = 1
    def parse(self, response):
        li_list = response.xpath('/html/body/div[1]/div/div/div/div[2]/ul/li')
        for li in li_list:
            title = li.xpath('./div[1]/a/@title').extract_first()
            href = 'https://www.4567kan.com/' + li.xpath('./div[1]/a/@href').extract_first()
            item = MvItem()
            item['title'] = title
            #mata是一個字典,蓋子點就可以傳遞給callback指定的回調函數
            yield scrapy.Request(href, callback=self.parse_detail, meta={'item': item})
        if self.pageNum <5:
            self.pageNum+=1
            new_url = format(self.url%self.pageNum)
            yield scrapy.Request(new_url,callback=self.parse)

    def parse_detail(self, response):
        item = response.meta['item']
        desc = response.xpath('/html/body/div[1]/div/div/div/div[2]/p[5]/span[2]/text()').extract_first()
        item['desc'] = desc
        yield  item

item.py

import scrapy


class MvItem(scrapy.Item):
    # define the fields for your item here like:
    title = scrapy.Field()
    desc = scrapy.Field()

管道

class MvPipeline:
    def process_item(self, item, spider):
        print(item)
        return item

12.Middleware中間件

下載中間件:批量

作用:批量攔截請求和響應

1.攔截請求:process_request

UA偽裝:

將所有的請求盡可能多的設定成不同的請求載體身份標識(一般直接在settings中加入,不在這里配置)
request.headers['User Agent'] = 'xxx'

批量實現

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"
]
    def process_request(self, request, spider):
        #從列表中隨機選擇一個
        request.headers['User-Agent']=random.choice(user_agent_list)

代理操作

PROXY_http = [
    '153.180.102.104:80',
    '195.208.131.189:56055',
]
PROXY_https = [
    '120.83.49.90:9000',
    '95.189.112.214:35508',
]
        if request.url.split(':')[0] == 'http':
            request.meta['proxy'] = 'http://' + random.choice(PROXY_http)  
        else:
            request.meta['proxy'] = 'https://' + random.choice(PROXY_https)
            

2.攔截異常:process_exception

如果代理ip報錯可以重新請求
    def process_exception(self, request, exception, spider):
        print('i am process_exception')
        # 攔截到異常的請求然后對其進行修正,然后重新進行請求發送
        # 代理操作
        if request.url.split(':')[0] == 'http':
            request.meta['proxy'] = 'http://' + random.choice(PROXY_http)  
        else:
            request.meta['proxy'] = 'https://' + random.choice(PROXY_https)  

        return request  # 將修正之后的請求進行重新發送

3.攔截響應:process_response

篡改響應數據或者直接替換響應對象

selenium在scrapy中的應用:

實例化瀏覽器對象:寫在爬蟲類的構造方法中
關閉瀏覽器:爬蟲類中的closed(self,spider)關閉瀏覽器
在中間件中執行瀏覽器自動化的操作

13.爬取網易新聞

爬取網易新聞的國內,國際,軍事,航空,無人機這五個板塊下對應的新聞標題和內容

分析:

每一個板塊對應頁面中的新聞數據是動態加載出來的

第一步:創建項目

scrapy startproject wangyiPro 
scrapy genspider wangyi www.baidu.com

第二步:修改文件

wangyi.py

import scrapy
from selenium import webdriver
from wangyiPro.items import WangyiproItem


class WangyiSpider(scrapy.Spider):
    name = 'wangyi'
    # allowed_domains = ['www.xxx.com']
    start_urls = ['https://news.163.com']
    five_model_urls = []
    bro = webdriver.Chrome(executable_path=r'C:\Users\zhui3\Desktop\chromedriver.exe')

    # 用來解析五個板塊對應的url,然后對其進行手動請求發送
    def parse(self, response):
        model_index = [3, 4, 6, 7, 8]
        li_list = response.xpath('//*[@id="index2016_wrap"]/div[1]/div[2]/div[2]/div[2]/div[2]/div/ul/li')
        for index in model_index:
            li = li_list[index]
            # 獲取了五個板塊對應的url
            model_url = li.xpath('./a/@href').extract_first()
            self.five_model_urls.append(model_url)
            # 對每一個板塊的url進行手動i請求發送
            yield scrapy.Request(model_url, callback=self.parse_model)

    # 解析每一個板塊頁面中的新聞標題和新聞詳情頁的url
    # 問題:response(不滿足需求的response)中並沒有包含每一個板塊中動態加載出的新聞數據
    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('./div/div[1]/h3/a/text()').extract_first()
            detail_url = div.xpath('./div/div[1]/h3/a/@href').extract_first()
            item = WangyiproItem()
            item['title'] = title
            # 對詳情頁發起請求解析出新聞內容
            yield scrapy.Request(detail_url, callback=self.parse_new_content, meta={'item': item})

    def parse_new_content(self, response):  # 解析新聞內容
        item = response.meta['item']
        content = response.xpath('//*[@id="endText"]//text()').extract()
        content = ''.join(content)

        item['content'] = content

        yield item

    # 最后執行
    def closed(self, spider):
        self.bro.quit()

items.py

import scrapy

class WangyiproItem(scrapy.Item):
    # define the fields for your item here like:
    title = scrapy.Field()
    content = scrapy.Field()

中間件

from time import sleep
from scrapy import signals
from scrapy.http import HtmlResponse


class WangyiproDownloaderMiddleware(object):

    def process_request(self, request, spider):
        return None

    def process_response(self, request, response, spider):#spider就是爬蟲文件中爬蟲類實例化的對象
        #進行所有響應對象的攔截
        #1.將所有的響應中那五個不滿足需求的響應對象找出
            #1.每一個響應對象對應唯一一個請求對象
            #2.如果我們可以定位到五個響應對應的請求對象后,就可以通過該i請求對象定位到指定的響應對象
            #3.可以通過五個板塊的url定位請求對象
            #總結: url==》request==》response

        #2.將找出的五個不滿足需求的響應對象進行修正(替換)
        #spider.five_model_urls:五個板塊對應的url
        bro = spider.bro
        if request.url in spider.five_model_urls:
            bro.get(request.url)
            sleep(1)
            page_text = bro.page_source #包含了動態加載的新聞數據
            #如果if條件程利則該response就是五個板塊對應的響應對象
            # HtmlResponse 篡改響應對象
            new_response = HtmlResponse(url=request.url,body=page_text,encoding='utf-8',request=request)
            return new_response
        return response

    def process_exception(self, request, exception, spider):

        pass

管道: 基於百度ai分類

from aip import AipNlp

""" 你的 APPID AK SK """
APP_ID = '219518'
API_KEY = 'rXTO5pFiBSoEtwYVl8cKH'
SECRET_KEY = 'oyxpRL7qyb9ubQC8nbsHpPGSfUV '


class WangyiproPipeline:
    client = AipNlp(APP_ID, API_KEY, SECRET_KEY)
    def process_item(self, item, spider):
        title = item['title']
        content = item['content']
        #UnicodeEncodeError: 'gbk' codec can't encode character '\xa0' in position 242: illegal multibyte sequence
        #報錯說不能被編碼,所以替換掉
        content = content.replace(u'\xa0',u'')
        title = title.replace(u'\xa0',u'')
        wd_dic = self.client.keyword(title,content)
        tp_dic = self.client.topic(title,content)
        print(wd_dic,tp_dic)
        return item

setting.py

USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'
ROBOTSTXT_OBEY = False
LOG_LEVEL = 'ERROR'
DOWNLOADER_MIDDLEWARES = {
   'wangyiPro.middlewares.WangyiproDownloaderMiddleware': 543,
}
ITEM_PIPELINES = {
   'wangyiPro.pipelines.WangyiproPipeline': 300,
}

run.py

from scrapy.cmdline import execute

execute(["scrapy", "crawl", "wangyi"])

14.基於CrawlSpider全站數據爬取

CrawlSpider就是爬蟲類中Spider的一個子類

直接項目:

爬取 陽光在線 標題處理狀態文本內容

1.創建項目

scrapy startproject sumpro

2.創建一個爬蟲文件:

scrapy genspider -t crawl sun www.xxxx.com

3.構造鏈接提取器和規則解析器

3.1鏈接提取器:

作用 : 可以根據指定的規則進行指定鏈接的提取
提取的規則:  allow =‘正則表達式’

3.2 規則解析器:

作用:獲取鏈接提取器提取到的鏈接,然后對其進行請求發送,根據指定規則對請求到的頁面
源碼數據進行數據解析

fllow=True:

將鏈接提取器 繼續作用到連接提取器提取出的頁碼鏈接 所對應的頁面中
注意:連接提取器和規則解析器也是一對一的關系

4.項目代碼

sun.py

from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from sumpro.items import SumproItem, SumproItem_second


class SunSpider(CrawlSpider):
    name = 'sun'
    # allowed_domains = ['www.xxxx.com']
    start_urls = ['http://wz.sun0769.com/political/index/politicsNewest?id=1&page=1']

    # 鏈接提取器
    Link = LinkExtractor(allow=r'id=1&page=\d+')
    Link_detail = LinkExtractor(allow=r'index\?id=\d+')
    rules = (
        # 實例化一個Rule(規則解釋器)的對象
        Rule(Link, callback='parse_item', follow=True),
        Rule(Link_detail, callback='parse_detail'),
    )

    def parse_item(self, response):
        li_list = response.xpath('/html/body/div[2]/div[3]/ul[2]/li')
        for i in li_list:
            title = i.xpath('./span[3]/a[1]/text()').extract_first()
            status = i.xpath('./span[2]/text()').extract_first()
            num = i.xpath('./span[1]/text()').extract_first()
            item = SumproItem_second()
            item['title'] = title
            item['status'] = status
            item['num'] = num
            yield item

    def parse_detail(self, response):
        content = response.xpath('/html/body/div[3]/div[2]/div[2]/div[2]/pre//text()').extract()
        content = ''.join(content)
        num = response.xpath('/html/body/div[3]/div[2]/div[2]/div[1]/span[4]/text()').extract_first()
        #num在詳情頁面里可能是空的
        if num:
            num = num.split(':')[-1]
            item = SumproItem()
            item['content'] = content
            item['num'] = num
            yield item

item.py

import scrapy

#為了讓content 和title status同時展示儲存 所以加了一個num
class SumproItem(scrapy.Item):
    # define the fields for your item here like:
    content = scrapy.Field()
    num = scrapy.Field()

class SumproItem_second(scrapy.Item):
    title = scrapy.Field()
    status = scrapy.Field()
    num = scrapy.Field()

管道

class SumproPipeline:
    def process_item(self, item, spider):
        if item.__class__.__name__ == 'SumproItem':
            content = item['content']
            num = item['num']
            print("內容" + content) #執行sql

        else:
            title = item['title']
            status = item['status']
            num = item['num']
            print("1" + title, "2"+status,"3"+num)

        return item

中間件,網站的反爬蟲是封ip,所以要設置代理ip

    def process_request(self, request, spider):
        request.meta['proxy'] = 'http://' + "218.91.7.82:43413"

setting 還要開啟中間件,管道,日志,不遵循協議,UA偽裝

BOT_NAME = 'sumpro'
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'
ROBOTSTXT_OBEY = False
LOG_LEVEL = 'ERROR'
SPIDER_MODULES = ['sumpro.spiders']
NEWSPIDER_MODULE = 'sumpro.spiders'
########################
ITEM_PIPELINES = {
   'sumpro.pipelines.SumproPipeline': 300,
}
DOWNLOADER_MIDDLEWARES = {
   'sumpro.middlewares.SumproDownloaderMiddleware': 543,
}

run.py

from scrapy.cmdline import execute

execute(["scrapy", "crawl", "sun"])

15.分布式爬蟲

什么是分布式爬蟲?

基於多台電腦組建一個分布式機群,然后讓機群中的每一台電腦執行同一組程序,然后讓它們對同一個
網站的數據進行分布爬取

為要使用分布式爬蟲?

提升爬取數據的效率

如何實現分布式爬蟲?

基於scrapy+redis的形式實現分布式
scrapy結合這scrapy-redis組建實現的分布式

原生的scrapy框架是無法實現分布式?

調度器無法被分布式機群共享
管道無法被共享

scrapy-redis組件的作用:

提供可以被共享的調度器和管道

1.環境安裝:

redis
pip Install scrapy-redis

2.編碼流程:

1.創建一個工程

scrapy startproject fbsPro

2.創建一個爬蟲文件

基於CrawlSpider的爬蟲文件
scrapy genspider -t crawl fbs www.xxxx.com

3.修改當前的爬蟲文件

- 導包:from scrapy_redis.spiders import RedisCrawlSpider
- 將當前爬蟲類的父類修改成RedisCrawlSpider
- 將start_urls替換成redis_key = 'xxx'#表示的是可被共享調度器中隊列的名稱
- 編寫爬蟲類爬取數據的操作

4.對settings進行配置:

-UA
	USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'
-指定管道:
#開啟可以被共享的管道
ITEM_PIPELINES = {
    'scrapy_redis.pipelines.RedisPipeline': 400
}
- 指定調度器:
#增加了一個去重容器類的配置, 作用使用Redis的set集合來存儲請求的指紋數據, 從而實現請求去重的持久化
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
#使用scrapy-redis組件自己的調度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
#配置調度器是否要持久化, 也就是當爬蟲結束了, 要不要清空Redis中請求隊列和去重指紋的set。如果是True, 就表示要持久化存儲, 就不清空數據, 否則清空數據
SCHEDULER_PERSIST = True
#指定redis的服務:
REDIS_HOST = 'redis服務的ip地址'
REDIS_PORT = 6379
# REDIS_PARAMS = {
#     'password': 'redisPasswordTest666666',
# }

#更改爬取速度
#CONCURRENT_REQUESTS = 2

5.redis的配置

進行配置:redis.conf
#bind 127.0.0.1

#關閉protected-mode模式,此時外部網絡可以直接訪問
protected-mode no
攜帶配置文件啟動redis服務	
./redis-server redis.conf
啟動redis的客戶端
redis-cli

6.執行當前的工程

進入到爬蟲文件對應的目錄中:
scrapy runspider fbs.py

7.向調度器隊列中仍入一個起始的url:

隊列在哪里呢?

答:隊列在redis中

lpush fbsQueue http://wz.sun0769.com/political/index/politicsNewest?id=1&page=1

8.置執行完成之后

lrange   fbs:items

9.代碼

fbs.py

import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from scrapy_redis.spiders import RedisCrawlSpider,RedisSpider
from fbsPro.items import FbsproItem


class FbsSpider(RedisCrawlSpider):
    name = 'fbs'
    # allowed_domains = ['www.xxxx.com']
    # start_urls = ['http://www.xxxx.com/']
    redis_key = 'fbsQueue'
    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 i in li_list:
            title = i.xpath('./span[3]/a[1]/text()').extract_first()
            status = i.xpath('./span[2]/text()').extract_first()
            item = FbsproItem()
            item['title'] = title
            item['status'] = status
            yield item

items.py

class FbsproItem(scrapy.Item):
    # define the fields for your item here like:
    title = scrapy.Field()
    status = scrapy.Field()

settings.py

USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'

BOT_NAME = 'fbsPro'

SPIDER_MODULES = ['fbsPro.spiders']
NEWSPIDER_MODULE = 'fbsPro.spiders'
ROBOTSTXT_OBEY = False
CONCURRENT_REQUESTS = 2
ITEM_PIPELINES = {
    'scrapy_redis.pipelines.RedisPipeline': 400
}

DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
SCHEDULER_PERSIST = True
REDIS_HOST = '127.0.0.1'
REDIS_PORT = 6379
# REDIS_PARAMS = {
#     'password': 'redisPasswordTest666666',
# }

16.增量式爬蟲

概念:

監測網站數據更新的情況。

核心:

去重!!!

深度爬取類型:

深度爬取類型的網站中需要對詳情頁的url進行記錄和檢測
記錄:將爬取過的詳情頁的url進行記錄保存
url存儲到redis的set中
檢測:如果對某一個詳情頁的url發起請求之前先要取記錄表中進行查看,該url是否存在,存在的話以為
    着這個url已經被爬取過了。

代碼

import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from redis import Redis
from zjs_moviePro.items import ZjsMovieproItem


class MovieSpider(CrawlSpider):
    name = 'movie'
    conn = Redis(host='127.0.0.1', port=6379)
    # allowed_domains = ['www.xxx.com']
    start_urls = ['https://www.4567tv.tv/index.php/vod/show/id/6.html']
    rules = (  # /index.php/vod/show/id/6/page/2.html
        Rule(LinkExtractor(allow=r'id/6/page/\d+\.html'), callback='parse_item', follow=False),
    )

    def parse_item(self, response):
        li_list = response.xpath('/html/body/div[1]/div/div/div/div[2]/ul/li')
        for li in li_list:
            name = li.xpath('./div/div/h4/a/text()').extract_first()
            detail_url = 'https://www.4567tv.tv' + li.xpath('./div/div/h4/a/@href').extract_first()
            ex = self.conn.sadd('movie_detail_urls', detail_url)
            if ex == 1:  # 向redis的set中成功插入了detail_url
                print('有最新數據可爬......')
                item = ZjsMovieproItem()
                item['name'] = name
                yield scrapy.Request(url=detail_url, callback=self.parse_detail, meta={'item': item})
            else:
                print('該數據已經被爬取過了!')

    def parse_detail(self, response):
        item = response.meta['item']
        desc = response.xpath('/html/body/div[1]/div/div/div/div[2]/p[5]/span[2]/text()').extract_first()
        item['desc'] = desc

        yield item

class ZjsMovieproItem(scrapy.Item):
    # define the fields for your item here like:
    name = scrapy.Field()
    desc = scrapy.Field()

class ZjsMovieproPipeline(object):
    def process_item(self, item, spider):
        conn = spider.conn
        conn.lpush('movie_data',item)
        return item

非深度爬取類型的網站:

名詞:數據指紋

一組數據的唯一標識


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM