scrapy shell使用方法(十)

回顾(九)

settings.py常用变量

1】settings.py中常用变量
    2.1) 设置日志级别
         LOG_LEVEL = ''
    2.2) 保存到日志文件(不在终端输出)
         LOG_FILE = ''
    2.3) 设置数据导出编码(主要针对于json文件)
         FEED_EXPORT_ENCODING = 'utf-8'
    2.4) 设置User-Agent
         USER_AGENT = ''
    2.5) 设置最大并发数(默认为16)
         CONCURRENT_REQUESTS = 32
    2.6) 下载延迟时间(每隔多长时间请求一个网页)
         DOWNLOAD_DELAY = 1
    2.7) 请求头
         DEFAULT_REQUEST_HEADERS = {'User-Agent':'Mozilla/'}
    2.8) 添加项目管道
         ITEM_PIPELINES = {'项目目录名.pipelines.类名':优先级}
    2.9) cookie(默认禁用,取消注释-True|False都为开启)
         COOKIES_ENABLED = False
    2.10) 非结构化数据存储路径
         IMAGES_STORE = '/home/tarena/images/'
         FILES_STORE = '/home/tarena/files/'
    2.11) 添加下载器中间件
        DOWNLOADER_MIDDLEWARES = {'项目名.middlewares.类名':200}

非结构化数据抓取

1】spider
    yield item['链接']2】pipelines.py
    from scrapy.pipelines.images import ImagesPipeline
    import scrapy
    class TestPipeline(ImagesPipeline):
       def get_media_requests(self,item,info):
            yield scrapy.Request(url=item['url'],meta={'name':item['name']})
            
       def file_path(self,request,response=None,info=None):
            name = request.meta['name']
            filename = name
            return filename
        
【3】settings.py
    IMAGES_STORE = 'D:/Spider/images'

Post请求

  • 方法

    scrapy.FormRequest(url=url,formdata=formdata,callback=self.xxx)
    
  • 使用cookie

    1】方法1
        COOKIES_ENABLED = False
        DEFAULT_REQUEST_HEADERS = {'Cookie':'xxxx'}2】方法2
        COOKIES_ENABLED = True
        yield scrapy.Request(url=url,cookies={},callback=self.xxxx)
        yield scrapy.FormRequest(url=url,formdata={},cookies={},callback=self.xxxx)3】方法3
        COOKIES_ENBALED = True
        class XxxCookieDownloaderMiddleware(object):
          def process_request(self,request,spider):
            request.cookies = {}
    

scrapy shell使用方法(十)

scrapy shell的使用

  • 定义+使用

    1】定义
        1.1) 调试蜘蛛的工具
        1.2) 交互式shell,可在不运行spider的前提下,快速调试 scrapy 代码(主要测试xpath表达式)2】基本使用
        scrapy shell URL地址
        
    【3】请求对象request属性
        3.1) request.url     : 请求URL地址
        3.2) request.headers : 请求头 - 字典
        3.3) request.meta    : item数据传递、定义代理
    
    【4】响应对象response属性
        4.1) response.url      : 返回实际数据的URL地址
        4.2) response.text     : 响应内容 - 字符串
        4.3) response.body     : 响应内容 - 字节串
        4.4) response.encoding :响应字符编码
        4.5) response.status   : HTTP响应码
    
  • scrapy.Request()参数

    1】url
    【2】callback
    【3】headers
    【4】meta :传递数据,定义代理
    【5】dont_filter :是否忽略域组限制,默认False,检查allowed_domains['']
        如果想忽略域组限制,则:dont_filter = True6】cookies
    

设置中间件(随机User-Agent)

  • 少量UA设置 - 不使用中间件

    1】方法一 : settings.py
        1.1) USER_AGENT = ''
        1.2) DEFAULT_REQUEST_HEADERS = {}2】方法二 : 爬虫文件
        yield scrapy.Request(url,callback=函数名,headers={})
    
  • 大量UA设置 - 使用middlewares.py中间件

    1】获取User-Agent方式
        1.1) 方法1 :新建useragents.py,存放大量User-Agent,random模块随机切换
        1.2) 方法2 :使用fake_useragent模块
            from fake_useragent import UserAgent
            agent = UserAgent().random
            
    【2】middlewares.py新建中间件类
    	class RandomUseragentMiddleware(object):
    		def process_request(self,reuqest,spider):
        		agent = UserAgent().random
        		request.headers['User-Agent'] = agent
                
    【3】settings.py添加此下载器中间件
    	DOWNLOADER_MIDDLEWARES = {'' : 优先级}
    

设置中间件(随机代理)

  • 代理IP中间件

    class RandomProxyDownloaderMiddleware(object):
        def process_request(self,request,spider):
        	request.meta['proxy'] = xxx
        
        # 捕获异常的方法,一旦代理不能用,会被此方法捕获,并重新包装请求再次发送
        def process_exception(self,request,exception,spider):
            return request
    

设置中间件(Cookie)

  • Cookie中间件

    class BaiduCookieDownloaderMiddleware(object):
        def process_request(self,request,spider):
            cookies = self.get_cookies()
            print('middleware3', cookies)
            # 利用请求对象request的cookies属性
            request.cookies = cookies
    
    
        def get_cookies(self):
            costr = ''
            cookies = {}
            for kv in costr.split('; '):
                cookies[kv.split('=')[0]] =kv.split('=')[1]
    
            return cookies
    
  • 练习

    将有道翻译案例的cookie使用中间件的方式来实现
    

分布式爬虫

  • 分布式爬虫介绍
1】原理
    多台主机共享1个爬取队列
    
【2】实现
    2.1) 重写scrapy调度器(scrapy_redis模块)
    2.2) sudo pip3 install scrapy_redis
  • 为什么使用redis

    1】Redis基于内存,速度快
    【2】Redis非关系型数据库,Redis中集合,存储每个request的指纹
    

scrapy_redis详解

  • GitHub地址

    https://github.com/rmax/scrapy-redis
    
  • settings.py说明

    # 重新指定调度器: 启用Redis调度存储请求队列
    SCHEDULER = "scrapy_redis.scheduler.Scheduler"
    
    # 重新指定去重机制: 确保所有的爬虫通过Redis去重
    DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
    
    # 不清除Redis队列: 暂停/恢复/断点续爬(默认清除为False,设置为True不清除)
    SCHEDULER_PERSIST = True
    
    # 优先级队列 (默认)
    SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue'
    #可选用的其它队列
    # 先进先出
    SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.FifoQueue'
    # 后进先出
    SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.LifoQueue'
    
    # redis管道
    ITEM_PIPELINES = {
        'scrapy_redis.pipelines.RedisPipeline': 300
    }
    
    #指定连接到redis时使用的端口和地址
    REDIS_HOST = 'localhost'
    REDIS_PORT = 6379
    

腾讯招聘分布式改写

  • 分布式爬虫完成步骤

    1】首先完成非分布式scrapy爬虫 : 正常scrapy爬虫项目抓取
    【2】设置,部署成为分布式爬虫
    
  • 分布式环境说明

    1】分布式爬虫服务器数量: 2(其中1台Windows,1台Ubuntu虚拟机)
    【2】服务器分工:
        2.1) Windows : 负责数据抓取
        2.2) Ubuntu  : 负责URL地址统一管理,同时负责数据抓取
    
  • 腾讯招聘分布式爬虫 - 数据同时存入1个Redis数据库

    1】完成正常scrapy项目数据抓取(非分布式 - 拷贝之前的Tencent)
    
    【2】设置settings.py,完成分布式设置
        2.1-必须) 使用scrapy_redis的调度器
             SCHEDULER = "scrapy_redis.scheduler.Scheduler"
            
        2.2-必须) 使用scrapy_redis的去重机制
             DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
            
        2.3-必须) 定义redis主机地址和端口号
             REDIS_HOST = '192.168.1.107'
             REDIS_PORT = 6379
            
        2.4-非必须) 是否清除请求指纹,True:不清除 False:清除(默认)
             SCHEDULER_PERSIST = True
            
        2.5-非必须) 在ITEM_PIPELINES中添加redis管道,数据将会存入redis数据库
             'scrapy_redis.pipelines.RedisPipeline': 2003】把代码原封不动的拷贝到分布式中的其他爬虫服务器,同时开始运行爬虫
    
    【结果】:多台机器同时抓取,数据会统一存到Ubuntu的redis中,而且所抓数据不重复
    
  • 腾讯招聘分布式爬虫 - 数据存入MySQL数据库

    """和数据存入redis步骤基本一样,只是变更一下管道和MySQL数据库服务器的IP地址"""1】settings.py
        1.1) SCHEDULER = 'scrapy_redis.scheduler.Scheduler'
        1.2) DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter'
        1.3) SCHEDULER_PERSIST = True
        1.4) REDIS_HOST = '192.168.1.105'
        1.5) REDIS_POST = 6379
        1.6) ITEM_PIPELINES = {'Tencent.pipelines.TencentMysqlPipeline' : 300}
        1.7) MYSQL_HOST = '192.168.1.105'2】将代码拷贝到分布式中所有爬虫服务器
    
    【3】多台爬虫服务器同时运行scrapy爬虫
    
    # 赠送腾讯MySQL数据库建库建表语句
    """
    create database tencentdb charset utf8;
    use tencentdb;
    create table tencenttab(
    job_name varchar(1000),
    job_type varchar(200),
    job_duty varchar(5000),
    job_require varchar(5000),
    job_address varchar(200),
    job_time varchar(200)
    )charset=utf8;
    """
    

新的篇章

腾讯招聘分布式改写之方法二

  • 使用redis_key改写(同时存入MySQL数据库)

    1】settings.py和方法一中写法一致
        1.1) SCHEDULER = 'scrapy_redis.scheduler.Scheduler'
        1.2) DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter'
        1.3) SCHEDULER_PERSIST = True
        1.4) REDIS_HOST = '192.168.1.107'
        1.5) REDIS_PORT = 6379
        1.6) ITEM_PIPELINES = {'Tencent.pipelines.TencentMysqlPipeline' : 300}
        1.7) MYSQL_HOST = '192.168.1.107'2】爬虫文件:tencent.py (必须基于start_urls)
        from scrapy_redis.spiders import RedisSpider
        class TencentSpider(RedisSpider):
            # 1. 去掉start_urls
            # 2. 定义redis_key
            redis_key = 'tencent:spider'
            def parse(self,response):
                pass3】把代码复制到所有爬虫服务器,并启动项目
    
    【4】到redis命令行,执行LPUSH命令压入第一个要爬取的URL地址
        >LPUSH tencent:spider 第1页的URL地址
    
    【注意】: 项目爬取结束后无法退出,如何退出?
    setting.py
    CLOSESPIDER_TIMEOUT = 3600
    # 到指定时间(3600秒)时,会自动结束并退出
    
  • 周期性计划任务-Linux

    1】进入周期性计划任务: crontab -e
    【2】设置周期性计划任务
        *  *  *  *  *  python3 /home/tarena/spider.py
        分 时 日 月 周
        
        分: 0-59: 0-23: 1-31: 1-12: 0-6
         
        , : 多个时间点
        - : 一个时间段
        / : 时间间隔频率
            
    【3】示例
        3.1) 每天早晨6:00去执行spider.py
             0 6 * * * python3 /home/tarena/spider.py
                
        3.2) 每天的09:0018:00去执行spider.py
             0 9,18 * * * python3 /home/tarena/spider.py
            
        3.3) 每天09:00-18:00之间每隔2小时,去执行一次spider.py
             0 9-18/2 * * * python3 /home/tarena/spider.py
    

机器视觉与tesseract

  • 概述

    1】作用
        处理图形验证码
    
    【2】三个重要概念 - OCR、tesseract-ocr、pytesseract
        2.1) OCR
            光学字符识别(Optical Character Recognition),通过扫描等光学输入方式将各种票据、报刊、书籍、文稿及其它印刷品的文字转化为图像信息,再利用文字识别技术将图像信息转化为电子文本
    
        2.2) tesseract-ocr
            OCR的一个底层识别库(不是模块,不能导入),由Google维护的开源OCR识别库
    
        2.3) pytesseract
            Python模块,可调用底层识别库,是对tesseract-ocr做的一层Python API封装
    
  • 安装tesseract-ocr

    1】Ubuntu安装
        sudo apt-get install tesseract-ocr
    
    【2】Windows安装
        2.1) 下载安装包
        2.2) 添加到环境变量(Path)3】测试(终端 | cmd命令行)
        tesseract xxx.jpg 文件名
    
  • 安装pytesseract

    1】安装
        sudo pip3 install pytesseract
        
    【2】使用示例
        import pytesseract
        # Python图片处理库
        from PIL import Image
    
        # 创建图片对象
        img = Image.open('test1.jpg')
        # 图片转字符串
        result = pytesseract.image_to_string(img)
        print(result)
    

在线打码平台

  • 为什么使用在线打码

    tesseract-ocr识别率很低,文字变形、干扰,导致无法识别验证码
    
  • 云打码平台使用步骤

    1】下载并查看接口文档
    【2】调整接口文档,调整代码并接入程序测试
    【3】真正接入程序,在线识别后获取结果并使用
    

破解云打码网站验证码

  • 1 - 下载并调整接口文档,封装成函数,打码获取结果

    import http.client, mimetypes, urllib, json, time, requests
    
    ######################################################################
    
    class YDMHttp:
    
        apiurl = 'http://api.yundama.com/api.php'
        username = ''
        password = ''
        appid = ''
        appkey = ''
    
        def __init__(self, username, password, appid, appkey):
            self.username = username  
            self.password = password
            self.appid = str(appid)
            self.appkey = appkey
    
        def request(self, fields, files=[]):
            response = self.post_url(self.apiurl, fields, files)
            response = json.loads(response)
            return response
        
        def balance(self):
            data = {'method': 'balance', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey}
            response = self.request(data)
            if (response):
                if (response['ret'] and response['ret'] < 0):
                    return response['ret']
                else:
                    return response['balance']
            else:
                return -9001
        
        def login(self):
            data = {'method': 'login', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey}
            response = self.request(data)
            if (response):
                if (response['ret'] and response['ret'] < 0):
                    return response['ret']
                else:
                    return response['uid']
            else:
                return -9001
    
        def upload(self, filename, codetype, timeout):
            data = {'method': 'upload', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey, 'codetype': str(codetype), 'timeout': str(timeout)}
            file = {'file': filename}
            response = self.request(data, file)
            if (response):
                if (response['ret'] and response['ret'] < 0):
                    return response['ret']
                else:
                    return response['cid']
            else:
                return -9001
    
        def result(self, cid):
            data = {'method': 'result', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey, 'cid': str(cid)}
            response = self.request(data)
            return response and response['text'] or ''
    
        def decode(self, filename, codetype, timeout):
            cid = self.upload(filename, codetype, timeout)
            if (cid > 0):
                for i in range(0, timeout):
                    result = self.result(cid)
                    if (result != ''):
                        return cid, result
                    else:
                        time.sleep(1)
                return -3003, ''
            else:
                return cid, ''
    
        def report(self, cid):
            data = {'method': 'report', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey, 'cid': str(cid), 'flag': '0'}
            response = self.request(data)
            if (response):
                return response['ret']
            else:
                return -9001
    
        def post_url(self, url, fields, files=[]):
            for key in files:
                files[key] = open(files[key], 'rb');
            res = requests.post(url, files=files, data=fields)
            return res.text
    
    ######################################################################
    def get_result(filename):
        # 用户名
        username    = 'yibeizi001'
        # 密码
        password    = 'zhishouzhetian001'
        # 软件ID,开发者分成必要参数。登录开发者后台【我的软件】获得!
        appid       = 1
        # 软件密钥,开发者分成必要参数。登录开发者后台【我的软件】获得!
        appkey      = '22cc5376925e9387a23cf797cb9ba745'
        # 验证码类型,# 例:1004表示4位字母数字,不同类型收费不同。请准确填写,否则影响识别率。在此查询所有类型 http://www.yundama.com/price.html
        codetype    = 5000
        # 超时时间,秒
        timeout     = 60
        # 初始化
        yundama = YDMHttp(username, password, appid, appkey)
        # 登陆云打码
        uid = yundama.login()
        # 查询余额
        balance = yundama.balance()
        # 开始识别,图片路径,验证码类型ID,超时时间(秒),识别结果
        cid, result = yundama.decode(filename, codetype, timeout)
    
        return result
    ######################################################################
    
  • 2 - 访问云打码网站,获取验证码并在线识别

    '''识别云打码官网的验证码'''
    from selenium import webdriver
    from ydmapi import *
    from PIL import Image
    
    class YdmSpider(object):
        def __init__(self):
            self.options = webdriver.ChromeOptions()
            # 浏览器窗口最大化
            self.options.add_argument('--start-maximized')
            self.browser = webdriver.Chrome(options=self.options)
    
            self.browser.get('http://www.yundama.com/')
    
        # 获取验证码图片截取出来
        def get_image(self):
            # 1.获取页面截图
            self.browser.save_screenshot('index.png')
            # 2.获取验证码节点坐标,把图片截取出来
            # location: 获取节点左上角的坐标(x y)
            location = self.browser.find_element_by_xpath('//*[@id="verifyImg"]').location
            # size: 获取节点的大小(宽度和高度)
            size = self.browser.find_element_by_xpath('//*[@id="verifyImg"]').size
            # 四个坐标
            left_x = location['x']
            left_y = location['y']
            right_x = left_x + size['width']
            right_y = left_y + size['height']
            # 从index.png中截图图片,注意crop()方法参数为元组
            img = Image.open('index.png').crop((left_x,left_y,right_x,right_y))
            img.save('verify.png')
    
        # 获取识别结果
        def get_result(self):
            result = get_result('verify.png')
            print('识别结果:',result)
    
        # 入口函数
        def run(self):
            self.get_image()
            self.get_result()
            self.browser.close()
    
    if __name__ == '__main__':
        spider = YdmSpider()
        spider.run()
    

Fiddler抓包工具

  • 配置Fiddler

    1】Tools -> Options -> HTTPS
        1.1) 添加证书信任:  勾选 Decrypt Https Traffic 后弹出窗口,一路确认
        1.2) 设置之抓浏览器的包:  ...from browsers only
    
    【2】Tools -> Options -> Connections
        2.1) 设置监听端口(默认为8888)
    
    【3】配置完成后重启Fiddler(重要)
        3.1) 关闭Fiddler,再打开Fiddler
    
  • 配置浏览器代理

    1】安装Proxy SwitchyOmega谷歌浏览器插件
    
    【2】配置代理
        2.1) 点击浏览器右上角插件SwitchyOmega -> 选项 -> 新建情景模式 -> myproxy(名字) -> 创建
        2.2) 输入  HTTP://  127.0.0.1  8888
        2.3) 点击 :应用选项
        
    【3】点击右上角SwitchyOmega可切换代理
    
    【注意】: 一旦切换了自己创建的代理,则必须要打开Fiddler才可以上网
    
  • Fiddler常用菜单

    1】Inspector :查看数据包详细内容
        1.1) 整体分为请求和响应两部分
        
    【2】Inspector常用菜单
        2.1) Headers :请求头信息
        2.2) WebForms: POST请求Form表单数据 :<body>
                       GET请求查询参数: <QueryString>
        2.3) Raw : 将整个请求显示为纯文本
    

移动端app数据抓取

  • 方法1 - 手机 + Fiddler

    设置方法见文件夹 - 移动端抓包配置
    
  • 方法2 - F12浏览器工具

有道翻译手机版破解案例

import requests
from lxml import etree

word = input('请输入要翻译的单词:')

post_url = 'http://m.youdao.com/translate'
post_data = {
  'inputtext':word,
  'type':'AUTO'
}

html = requests.post(url=post_url,data=post_data).text
parse_html = etree.HTML(html)
xpath_bds = '//ul[@id="translateResult"]/li/text()'
result = parse_html.xpath(xpath_bds)[0]

print(result)

基础爬虫总结

# 1、什么是爬虫
  爬虫是请求网站并提取数据的自动化程序

# 2、robots协议是什么
  爬虫协议或机器人协议,网站通过robots协议告诉搜索引擎哪些页面可以抓取,哪些页面不能抓取

# 3、爬虫的基本流程
  1、请求得到响应
  2、解析
  3、保存数据

# 4、请求
  1、urllib
  2、requests
  3、scrapy

# 5、解析
  1、re正则表达式
  2、lxml+xpath解析
  3、json解析模块

# 6、selenium+browser

# 7、常见反爬策略
  1、Headers : 最基本的反爬手段,一般被关注的变量是UserAgent和Referer,可以考虑使用浏览器中
  2、UA : 建立User-Agent池,每次访问页面随机切换
  3、拉黑高频访问IP
     数据量大用代理IP池伪装成多个访问者,也可控制爬取速度
  4、Cookies
     建立有效的cookie池,每次访问随机切换
  5、验证码
    验证码数量较少可人工填写
    图形验证码可使用tesseract识别
    其他情况只能在线打码、人工打码和训练机器学习模型
  6、动态生成
    一般由js动态生成的数据都是向特定的地址发get请求得到的,返回的一般是json
  7、签名及js加密
    一般为本地JS加密,查找本地JS文件,分析,或者使用execjs模块执行JS
  8、js调整页面结构
  9、js在响应中指向新的地址

# 8、scrapy框架的运行机制

# 9、分布式爬虫的原理
  多台主机共享一个爬取队列
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大大枫free

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值