爬蟲(十)--增量爬蟲,分佈式,驗證碼,fiddler,自定義監控

一、增量爬蟲

增量爬蟲:就是使爬蟲獲取到的數據以增量的形式穩定增長。

增量爬蟲的核心,就是去重。

(一)方案

1.爬蟲結束

在保存到數據庫之前,查看數據是否重複,或者用update方法做更新操作。

2.爬蟲開始

在爬取數據前,查看這個url是否被爬過。

3.爬蟲中間

有時候在爬取一些網站的時候,可能得到了一些數據,但是這個網頁的更新速度比較快,這時候我們可以查看這個網頁是否有更新,如果更新了,響應的網站的數據就應該在數據庫更新。

實現步驟

將頁面的response.text用hash算法得到一個加密串,如果頁面更新,response.text內容肯定會發生變化,加密串就也會發生變化,所以可以通過判斷這個加密串是否重複,來判斷是否更新。

(二)去重方法

1.python中的set集合(×)

項目今天運行了,會在代碼中產生一個set,當程序關閉,set就失效了,明天如果運行,還是從頭開始,沒有去重的意義。

2.redis(√)

原因

  1. redis有一個數據類型叫做set,這個set不允許重複
  2. redis是一個可以持久化的數據庫,可以將這些已經爬取的信息保存下來,下次運行就能找到
  3. redis是運行在內存上的,速度快,對程序影響小

做法

import hashlib,redis
def get_md5(value):
	md5 = hashlib.md5()
	md5.update(value.encode())
	return md5.hexdigest()

# True表示重複
def before_request(url):
	red = redis.Redis()
	res = red.sadd('項目名:urlset',get_md5(url))
	if res == 0:
		return True
	return False

項目–貓眼電影演員信息無限抓取(生產者消費者模式)

oop(增量爬蟲–判斷url是否被爬過)

import hashlib
from queue import Queue

import redis
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from lxml import etree
class Maoyan_actor():
    def __init__(self,q):
        self.q = q
        self.driver = webdriver.PhantomJS()
        self.wait = WebDriverWait(self.driver,20)

        self.parse()

    def get_md5(self,value):
        md5 = hashlib.md5()
        md5.update(value.encode('utf-8'))
        return md5.hexdigest()

    # true表示重複
    def request_seen(self,url):

        red = redis.Redis()
        res = red.sadd('maoyan:actor_url', self.get_md5(url))
        if res == 0:
            return True
        return False

    def get_text(self,text):
        if text:
            return text[0]
        return ''

    def get_xpath_by_selenium(self,url,xpath):
        self.driver.get(url)
        webelement =  self.wait.until(EC.presence_of_element_located((By.XPATH,xpath)))
        webelement.click()
        return etree.HTML(self.driver.page_source)
    def parse(self):
        while True:
            if self.q.empty():
                break
            url = self.q.get()
            #如果url所對應的演員已經爬取過了,跳過本次,繼續下載
            if self.request_seen(url):
                continue
            html = self.get_xpath_by_selenium(url,'//div[@class="introduce"]/div[2]/a')
            #解析內容
            name = self.get_text(html.xpath('//div[@class="celeInfo-list"]/dl/dd[1]/text()'))
            print(name)

            #取出相關電影人,將其加入隊列。
            url_list = html.xpath('//div[@class="item"]/div/a/@href')
            # print(url_list)
            for u in url_list:
                self.q.put('https://maoyan.com'+u)


if __name__ == '__main__':
    # base_url = 'https://maoyan.com/films/celebrity/29190'
    #初始url列表
    start_urls = [
        'https://maoyan.com/films/celebrity/29515',#歐美
        'https://maoyan.com/films/celebrity/29190',#中國
        'https://maoyan.com/films/celebrity/31444',#日本
    ]
    q = Queue()
    #初始化隊列,將來所有的url都是從隊列裏面取的
    for url in start_urls:
        q.put(url)
    Maoyan_actor(q)

多線程(增量爬蟲–判斷頁面是否更新)

import hashlib
from queue import Queue
import threading
import redis
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from lxml import etree
class Maoyan_actor(threading.Thread):
    def __init__(self,name,q):
        super().__init__()
        self.q = q
        self.name = name
        self.driver = webdriver.PhantomJS()
        self.wait = WebDriverWait(self.driver,20)

        # self.parse()
    def run(self):
        self.parse()
    def get_md5(self,value):
        md5 = hashlib.md5()
        md5.update(value.encode('utf-8'))
        return md5.hexdigest()

    def response_seen(self,content):
        red = redis.Redis()
        res = red.sadd('maoyan:actor_content', self.get_md5(content))
        if res == 0:
            return True
        return False
    # true表示重複
    def request_seen(self,url):

        red = redis.Redis()
        res = red.sadd('maoyan:actor_url', self.get_md5(url))
        if res == 0:
            return True
        return False

    def get_text(self,text):
        if text:
            return text[0]
        return ''

    def get_xpath_by_selenium(self,url,xpath):
        self.driver.get(url)
        webelement =  self.wait.until(EC.presence_of_element_located((By.XPATH,xpath)))
        webelement.click()
        html_str = self.driver.page_source
        #查看頁面內容是否更新
        if not self.response_seen(html_str):
            return etree.HTML(html_str)
        return None
    def parse(self):
        while True:
            if self.q.empty():
                break
            url = self.q.get()

            #如果url所對應的演員已經爬取過了,跳過本次,繼續下載
            # if self.request_seen(url):
            #     continue
            try:
                html = self.get_xpath_by_selenium(url, '//div[@class="introduce"]/div[2]/a')
                # 解析內容
                name = self.get_text(html.xpath('//div[@class="celeInfo-list"]/dl/dd[1]/text()'))
                print('{}==============================={}'.format(name, self.name))
                print(name)

                # 取出相關電影人,將其加入隊列。
                url_list = html.xpath('//div[@class="item"]/div/a/@href')
                # print(url_list)
                for u in url_list:
                    self.q.put('https://maoyan.com' + u)
            except Exception:
                print('頁面爲空!')


if __name__ == '__main__':
    # base_url = 'https://maoyan.com/films/celebrity/29190'
    #初始url列表
    start_urls = [
        'https://maoyan.com/films/celebrity/29515',#歐美
        'https://maoyan.com/films/celebrity/29190',#中國
        'https://maoyan.com/films/celebrity/31444',#日本
    ]
    q = Queue()
    #初始化隊列,將來所有的url都是從隊列裏面取的
    for url in start_urls:
        q.put(url)
    #創建列表
    crawl_list = ['aa','bb','cc']
    for crawl in crawl_list:
        Maoyan_actor(crawl,q).start()

異常出現位置:一般異常都是在所在線程裏面出現,一旦出現,該線程如果沒有處理異常的代碼,該線程就會斷掉。

所以在工作中,一定要寫處理異常的代碼。

二、scrapy分佈式–筆趣閣

(一)什麼是分佈式

將一個任務分割成多份,每一份由一個計算機完成,最後所有的計算機能夠成爲一個整體,得到這個任務的結果。

分佈式數據

產生的原因

原來一個數據庫都是放在一臺電腦上,但是由於用戶量的增多,造成數據庫壓力很大。所以產生了一個思想,就是用多臺電腦可以提供同樣的數據庫服務。

(二)scrapy分佈式

原來的項目都是部署在一臺電腦上的,這樣爬取速度雖然很快,但是我們還能提升,聯想到分佈式的思想,我們可以通過多臺電腦進行配合,使我們的爬取速度大幅度提升。

(三)scrapy和scrapy-redis的區別

  1. scrapy是一個通用爬蟲框架,但不支持分佈式
  2. scrapy-redis,在scrapy的基礎上增加一個redis組件,這個redis裏面設置了待爬取的url列表和存放每個url的hash值的指紋集合。通過這個做到了分佈式,使得多臺電腦可以聯合爬取一個任務。
    在這裏插入圖片描述

(四)scrapy分佈式部署步驟

1.導包

from scrapy_redis.spiders import RedisSpider

更改spider繼承,讓spider繼承RedisSpider

2.初始url

註釋start_urls,新建一個py文件,將url存儲到redis數據中–設置任務

3.在spider增加類變量redis_key

4.在settings中進行配置

主機

# 配置scrapy-redis調度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 配置url去重
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
ITEM_PIPELINES = {
'scrapy_redis.pipelines.RedisPipeline': 300
}
# 優先級隊列
SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue'
# 主機名
REDIS_HOST = 'localhost'
# 端口號
REDIS_PORT = 6379

MONGO_URL = 'localhost'
MONGO_DATABASE = 'biquge'

從機

1.將star_urls初始化的代碼全部註釋
2.從機的redis可以關閉

SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 去重
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 優先級隊列
SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue'

# ITEM_PIPELINES = {
#     'scrapy_redis.pipelines.RedisPipeline': 300
# }

REDIS_HOST = '主機ip'
REDIS_PORT = 6379

MONGO_URL = '主機ip'
MONGO_DATABASE = 'biquge'

三、驗證碼

步驟

1.安裝tesseract

2.配置兩個環境變量

系統變量

在這裏插入圖片描述
在這裏插入圖片描述

用戶變量

在這裏插入圖片描述
在這裏插入圖片描述

3.驗證

在這裏插入圖片描述

4.安裝pytesseract

在這裏插入圖片描述

5.修改源代碼

在這裏插入圖片描述
在這裏插入圖片描述

6.使用

import pytesseract
from PIL import Image

image = Image.open('CheckCode.png')
# 把一個彩色圖變成灰度圖
image = image.convert('L')
tessdata_dir_config = '--tessdata-dir "D:\\Tesseract-OCR\\tessdata"'

# 去除干擾線
threshold = 128
table = []
for i in range(256):
    if i < threshold:
        table.append(0)
    else:
        table.append(1)
image = image.point(table,'1')
image_str = pytesseract.image_to_string(image,config=tessdata_dir_config)
print(image_str)

四、Fiddler

步驟

1.配置

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

2.下載證書

在這裏插入圖片描述

3.下載好後,打開並安裝

4.重啓fiddler

在這裏插入圖片描述
在這裏插入圖片描述

五、爬蟲的自定義監控

自定義監控:當我們寫好一個爬蟲,它可能以後也會運行。如果頁面發生變更,可以自動告訴我們這個爬蟲頁面內容獲取不到了,需要更改代碼。同時如果爬蟲出現故障,也可以告訴我們爬蟲出了什麼問題。

自定義監控的主要方法就是用郵件的形式讓爬蟲通知我們。

重點:如何發郵件

項目–筆趣閣

需求:
從biquge數據庫中,隨機選出一本小說,並將小說的內容整合在一起。用郵件的形式發送給我。

import smtplib
import pymongo,random
from email.mime.text import MIMEText

client = pymongo.MongoClient()
db = client['biquge']

def send_email(text):
	# 發件人
    msg_from = '[email protected]'
    # 授權碼
    password = 'xxxxxxxxxxxxxxxx'
    # 收件人,可多個,用','隔開
    receiver = '[email protected]'
    subject = '每天一本小說'
    msg = MIMEText(text,'plain','utf-8')
    msg['Subject'] = subject
    msg['From'] = msg_from
    msg['To'] = receiver
    try:
        smtp = smtplib.SMTP()
        # 連接郵件服務器
        smtp.connect('smtp.qq.com')
        # 登錄
        smtp.login(msg_from,password)
        # 發送
        smtp.sendmail(msg_from,msg['To'].split(','),msg.as_string())
        print('發送成功!')
    except Exception:
        print('發送失敗!')

def get_novel():
    # 隨機找出一本書
    title_list = []
    db_titles = db['xuanhuan'].find({},{'_id':0,'title':1})
    for title in db_titles:
        if title['title'] in title_list:
            continue
        title_list.append(title['title'])
    # 隨機選一本小說
    title = random.choice(title_list)
    # 通過小說名查找所有該書的數據
    ones = db['xuanhuan'].find({'title':title})
    text = ''
    for one in ones:
        for k,v in one['content'].items():
            text += k + '\n' + v.replace('\r','\n')
    send_email(text)

if __name__ == '__main__':
    get_novel()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章