爬虫(十)--增量爬虫,分布式,验证码,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()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章