由于互联网的极速发展,所有现在的信息处于大量堆积的状态,我们既要向外界获取大量数据,又要在大量数据中过滤无用的数据。针对我们有益的数据需要我们进行指定抓取,从而出现了现在的爬虫技术,通过爬虫技术我们可以快速获取我们需要的数据。但是在这爬虫过程中,信息拥有方会对爬虫进行反爬处理,我们就需要对这些困难进行逐个击破。

刚好前段时间做了爬虫相关的工作,这里就记录下一些相关的心得。

本文案例代码地址 https://github.com/yangtao9502/ytaoCrawl

这里我是使用的 Scrapy 框架进行爬虫,开发环境相关版本号:

Scrapy       : 1.5.1
lxml         : 4.2.5.0
libxml2      : 2.9.8
cssselect    : 1.0.3
parsel       : 1.5.1
w3lib        : 1.20.0
Twisted      : 18.9.0
Python       : 3.7.1 (default, Dec 10 2018, 22:54:23) [MSC v.1915 64 bit (AMD64)]
pyOpenSSL    : 18.0.0 (OpenSSL 1.1.1a  20 Nov 2018)
cryptography : 2.4.2
Platform     : Windows-10-10.0.15063-SP0

本地开发环境建议使用 Anaconda 安装相关环境,否则可能出现各种依赖包的冲突,相信遇到过的都深有体会,在你配置相关环境的时候就失去爬虫的兴趣。
本文提取页面数据主要使用 Xpath ,所以在进行文中案例操作前,先了解 Xpath 的基本使用。

创建 Scrapy 项目

scrapy 创建项目很简单,直接一条命令搞定,接下来我们创建 ytaoCrawl 项目:

scrapy startproject ytaoCrawl

注意,项目名称必须以字母开头,并且只包含字母、数字和下划线。
创建成功后界面显示:

初始化项目的文件有:

其中各个文件的用途:

  • spider 目录用于存放爬虫文件。
  • items.py 文件最为对象,将爬虫数据保存在该对象中。
  • middlewares.py 文件为中间件处理器,比如请求和响应的转换都在里面实现。
  • pipelines.py 文件为数据管道,用于数据抓取后输送。
  • settings.py 文件为配置文件,爬虫中的一些配置可在该文件中设置。
  • scrapy.cfg 文件为爬虫部署的配置文件。

了解几个默认生成的文件后再看下面的 scrapy 结构原理图,相对好理解。

这样我们的一个 scrapy 爬虫项目就此创建完成。

创建 Spider

我们先创建一个 python 文件 ytaoSpider,该类必须继承 scrapy.Spider 类。接下来我们就以爬取北京 58 租房信息为例进行分析。

#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# @Author  : YangTao
# @blog    : https://ytao.top
#
import scrapy

class YtaoSpider(scrapy.Spider):
    # 定义爬虫名称
    name = "crawldemo"
    # 允许爬取的域名,但不包含 start_urls 中的链接
    allowed_domains = ["58.com"]
    # 起始爬取链接
    start_urls = [
        "https://bj.58.com/chuzu/?PGTID=0d100000-0038-e441-0c8a-adeb346199d8&ClickID=2"
    ]

    def download(self, response, fName):
        with open(fName + ".html", 'wb') as f:
            f.write(response.body)

    # response 是返回抓取后的对象
    def parse(self, response):
        # 下载北京租房页面到本地,便于分析
        self.download(response, "北京租房")

通过执行命令启动爬虫,指定爬虫名字:

scrapy crawl crawldemo

当我们有多个爬虫时,可以通过 scrapy list 获取所有的爬虫名。

开发过程中当然也可以用 mian 函数在编辑器中启动:

if __name__ == '__main__':
    name = YtaoSpider.name
    cmd = 'scrapy crawl {0} '.format(name)
    cmdline.execute(cmd.split())

这时将在我们启动的目录中下载生成我们爬取的页面。

翻页爬取

上面我们只爬取到了第一页,但是我们实际抓取数据过程中,必定会涉及到分页,所以观察到该网站的分页是将最后一页有展示出来(58最多只展示前七十页的数据),如图。

从下图观察到分页的 html 部分代码。

接下来通过 Xpath 和正则匹配获取最后一页的页码。

def pageNum(self, response):
    # 获取分页的 html 代码块
    page_ele = response.xpath("//li[@id='pager_wrap']/div[@class='pager']")
    # 通过正则获取含有页码数字的文本
    num_eles = re.findall(r">\d+<", page_ele.extract()[0].strip())
    # 找出最大的一个
    count = 0
    for num_ele in num_eles:
        num_ele = str(num_ele).replace(">", "").replace("<", "")
        num = int(num_ele)
        if num > count:
            count = num
    return count

通过对租房链接进行分析,可以看出不同页码的链接为https://bj.58.com/chuzu/pn+num 这里的num代表页码,我们进行不同的页码抓取时,只需更换页码即可,parse 函数可更改为:

# 爬虫链接,不含页码
target_url = "https://bj.58.com/chuzu/pn"

def parse(self, response):
        print("url: ", response.url)
        num = self.pageNum(response)
        # 开始页面本来就是第一页,所以在遍历页面时,过滤掉第一页
        p = 1
        while p < num:
            p += 1
            try:
                # 拼接下一页链接
                url = self.target_url + str(p)
                # 进行抓取下一页
                yield Request(url, callback=self.parse)
            except BaseException as e:
                logging.error(e)
                print("爬取数据异常:", url)

执行后,打印出的信息如图:

因为爬虫是异步抓取,所以我们的打印出来的并非有序数据。
上面所介绍的是通过获取最后一页的页码进行遍历抓取,但是有些网站没有最后一页的页码,这时我们可以通过下一页来判断当前页是否为最后一页,如果不是,就获取下一页所携带的链接进行爬取。

获取数据

这里我们就获取标题,面积,位置,小区,及价格信息,我们需要先在 item 中创建这些字段,闲话少说,上代码。

# 避免取xpath解析数据时索引越界
def xpath_extract(self, selector, index):
    if len(selector.extract()) > index:
        return selector.extract()[index].strip()
    return ""

def setData(self, response):
    items = []
    houses = response.xpath("//ul[@class='house-list']/li[@class='house-cell']")
    for house in houses:
        item = YtaocrawlItem()
        # 标题
        item["title"] = self.xpath_extract(house.xpath("div[@class='des']/h2/a/text()"), 0)
        # 面积
        item["room"] = self.xpath_extract(house.xpath("div[@class='des']/p[@class='room']/text()"), 0)
        # 位置
        item["position"] = self.xpath_extract(house.xpath("div[@class='des']/p[@class='infor']/a/text()"), 0)
        # 小区
        item["quarters"] = self.xpath_extract(house.xpath("div[@class='des']/p[@class='infor']/a/text()"), 1)
        money = self.xpath_extract(house.xpath("div[@class='list-li-right']/div[@class='money']/b/text()"), 0)
        unit = self.xpath_extract(house.xpath("div[@class='list-li-right']/div[@class='money']/text()"), 1)
        # 价格
        item["price"] = money+unit
        items.append(item)
    return items

def parse(self, response):
    items = self.setData(response)
    for item in items:
        yield item

    # 接着上面的翻页操作 .....

至此,我们以获取我们想要的数据,通过打印 parse 中的 item 可看到结果。

数据入库

我们已抓取到页面的数据,接下来就是将数据入库,这里我们以 MySQL 存储为例,数据量大的情况,建议使用使用其它存储产品。
首先我们先在 settings.py 配置文件中设置 ITEM_PIPELINES 属性,指定 Pipeline 处理类。

ITEM_PIPELINES = {
    # 值越小,优先级调用越高
   'ytaoCrawl.pipelines.YtaocrawlPipeline': 300,
}

在 YtaocrawlPipeline 类中处理数据持久化,这里 MySQL 封装工具类 mysqlUtils 代码可在 github 中查看。
通过再 YtaoSpider#parse 中使用 yield 将数据传输到 YtaocrawlPipeline#process_item 中进行处理。

class YtaocrawlPipeline(object):

    def process_item(self, item, spider):
        table = "crawl"
        item["id"] = str(uuid.uuid1())
        # 如果当前爬取信息的链接在库中有存在,那么就删除旧的再保存新的
        list = select(str.format("select * from {0} WHERE url = '{1}'", table, item["url"]))
        if len(list) > 0:
            for o in list:
                delete_by_id(o[0], table)
        insert(item, table)
        return item

在数据库中,可以看到成功抓取到数据并入库。

反爬机制应对

既然有数据爬虫的需求,那么就一定有反扒措施,就当前爬虫案例进行一下分析。

字体加密

通过上面数据库数据的图,可以看到该数据中存在乱码,通过查看数据乱码规律,可以定位在数字进行了加密。

同时,通过打印数据可以看到\xa0字符,这个(代表空白符)在 ASCII 字符 0x20~0x7e 范围,可知是转换为了 ASCII 编码。

因为知道是字体加密,所以在下载的页面查看font-family字体时,发现有如下图所示代码:

看到这个fangchan-secret字体比较可疑了,它是在js中动态生成的字体,且以 base64 存储,将以下字体进行解码操作。

if __name__ == '__main__':
    secret = "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"
    # 将字体文件编码转换为 UTF-8 编码的字节对象
    bytes = secret.encode(encoding='UTF-8')
    # base64位解码
    decodebytes = base64.decodebytes(bytes)
    # 利用 decodebytes 初始化 BytesIO,然后使用 TTFont 解析字体库
    font = TTFont(BytesIO(decodebytes))
    # 字体的映射关系
    font_map = font['cmap'].tables[0].ttFont.tables['cmap'].tables[0].cmap

    print(font_map)

通过将 fontTools 库的 TTFont 将字体进行解析,都到如下字体映射结果:

{
    38006: 'glyph00007',
    38287: 'glyph00005',
    39228: 'glyph00006',
    39499: 'glyph00003',
    40506: 'glyph00010',
    40611: 'glyph00001',
    40804: 'glyph00009',
    40850: 'glyph00004',
    40868: 'glyph00002',
    40869: 'glyph00008'
}

刚好十个映射,对应的 0~9 的数量,但是查找相应规律,1~9 后,出现了个 10,那么这里对应的数字到底是一个怎么样的规律呢?还有上面映射对应的 key 不是16进制的 ASCII 码,而是一个纯数字,是不是可能是十进制的码呢?
接下来验证我们的设想,将页面上获取的十六进制的码转换成十进制的码,然后去匹配映射中的数据,发现映射的值的非零数字部分刚好比页面上对应的数字字符大 1 ,可知,真正的值需要我们在映射值中减 1。
代码整理后

def decrypt(self, response, code):
    secret = re.findall("charset=utf-8;base64,(.*?)'\)", response.text)[0]
    code = self.secretfont(code, secret)
    return code

def secretfont(self, code, secret):
    # 将字体文件编码转换为 UTF-8 编码的字节对象
    bytes = secret.encode(encoding='UTF-8')
    # base64位解码
    decodebytes = base64.decodebytes(bytes)
    # 利用 decodebytes 初始化 BytesIO,然后使用 TTFont 解析字体库
    font = TTFont(BytesIO(decodebytes))
    # 字体的映射关系
    font_map = font['cmap'].tables[0].ttFont.tables['cmap'].tables[0].cmap
    chars = []
    for char in code:
        # 将每个字符转换成十进制的 ASCII 码
        decode = ord(char)
        # 如果映射关系中存在 ASCII 的 key,那么这个字符就有对应的字体
        if decode in font_map:
            # 获取映射的值
            val = font_map[decode]
            # 根据规律,获取数字部分,再减1得到真正的值
            char = int(re.findall("\d+", val)[0]) - 1
        chars.append(char)
    return "".join(map(lambda s:str(s), chars))

现在,我们将所有爬取的数据进行解密处理,再查看数据:

上图中,进行解密后,完美解决数据乱码!

验证码和封禁IP

验证码一般分为两类,一类是刚开始进入时,必须输入验证码的,一类是频繁请求后,需要验证码验证再继续接下来的请求。
对于第一种来说,就必须破解它的验证码才能继续,第二种来说,除了破解验证码,还可以使用代理进行绕过验证。
对于封禁IP的反爬,同样可使用代理进行绕过。比如还是使用上面的网址爬虫,当它们识别到我可能是爬虫时,就会使用验证码进行拦截,如下图:

接下来,我们使用随机 User-Agent 和代理IP进行绕行。
先设置 settings.USER_AGENT,注意PC端和移动端不要混合设置的 User-Agent,否则你会爬取数据会异常,因为不同端的页面不同

USER_AGENT = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.10 Safari/537.36",
    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
    # ......
]    

在请求中设置随机 User-Agent 中间件

class RandomUserAgentMiddleware(object):
    def __init__(self, agents):
        self.agent = agents

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            agents=crawler.settings.get('USER_AGENT')
        )

    def process_request(self, request, spider):
        # 随机获取设置中的一个 User-Agent
        request.headers.setdefault('User-Agent', random.choice(self.agent))

设置动态IP中间件

class ProxyIPMiddleware(object):
    def __init__(self, ip=''):
        self.ip = ip

    def process_request(self, request, spider):
        # 如果当前的地址重定向到了验证码地址,就使用代理ip进行重新请求
        if self.ban_url(request.url):
            # 获取被重定向的地址
            redirect_urls = request.meta.get("redirect_urls")[0]
            # 将当前重定向到验证码的地址改为原始请求地址
            request._set_url(redirect_urls)
            # 设置动态代理,这里在线上一般使用接口动态生成代理
            request.meta["proxy"] = "http://%s" % (self.proxy_ip())

    def ban_url(self, url):
        # settings中设置的验证码或被禁止的页面链接,当遇到该链接时,爬虫会进行绕行重爬
        dic = settings.BAN_URLS
        # 验证当前请求地址是否为验证码地址
        for d in dic:
            if url.find(d) != -1:
                return True
        return False

    # 代理动态生成的 ip:port
    def proxy_ip(self):
        # 模拟动态生成代理地址
        ips = [
            "127.0.0.1:8888",
            "127.0.0.1:8889",
        ]
        return random.choice(ips);

    def process_response(self, request, response, spider):
        # 如果不是成功响应,则重新爬虫
        if response.status != 200:
            logging.error("失败响应: "+ str(response.status))
            return request
        return response

最后在 settings 配置文件中开启这些中间件。

DOWNLOADER_MIDDLEWARES = {
   'ytaoCrawl.middlewares.RandomUserAgentMiddleware': 500,
   'ytaoCrawl.middlewares.ProxyIPMiddleware': 501,
   'ytaoCrawl.middlewares.YtaocrawlDownloaderMiddleware': 543,
}

现在为止,设置随机 User-Agent 和动态IP绕行已完成。

部署

使用 scrapyd 部署爬虫项目,可以对爬虫进行远程管理,如启动,关闭,日志调用等等。
部署前,我们得先安装 scrapyd ,使用命令:

pip install scrapyd

安装成功后,可以看到该版本为 1.2.1

部署后,我们还需要一个客户端进行访问,这里就需要一个 scrapyd-client 客户端:

pip install scrapyd-client

修改 scrapy.cfg 文件

[settings]
default = ytaoCrawl.settings

[deploy:localytao]
url = http://localhost:6800/
project = ytaoCrawl

# deploy 可批量部署

启动 scrapyd:

scrapyd

如果是 Windows,要先在X:\xx\Scripts下创建scrapyd-deploy.bat文件

@echo off
"X:\xx\python.exe" "X:\xx\Scripts\scrapyd-deploy" %1 %2

项目部署到 Scrapyd 服务上:

scrapyd-deploy localytao -p ytaoCrawl

远程启动
curl http://localhost:6800/schedule.json -d project=ytaoCrawl -d spider=ytaoSpider

执行启动后,可以在http://localhost:6800/中查看爬虫执行状态,以及日志

除了启动可远程调用外,同时 Scrapyd 还提供了较丰富的 API:

  • 服务中爬虫状态查询 curl http://localhost:6800/daemonstatus.json
  • 取消爬虫 curl http://localhost:6800/cancel.json -d project=projectName -d job=jobId
  • 展示项目 curl http://localhost:6800/listprojects.json
  • 删除项目 curl http://localhost:6800/delproject.json -d project=projectName
  • 展示爬虫 curl http://localhost:6800/listspiders.json?project=projectName
  • 获取项目所有版本号 curl http://localhost:6800/listversions.json?project=projectName
  • 删除项目版本号 curl http://localhost:6800/delversion.json -d project=projectName -d version=versionName

更多详情 https://scrapyd.readthedocs.io/en/stable/api.html

总结

本文篇幅有限,剖析过程中不能面面俱到,有些网站的反爬比较棘手的,只要我们一一分析,都能找到破解的办法,还有眼睛看到的数据并不一定是你拿到的数据,比如有些网站的html渲染都是动态的,就需要我们去处理好这些信息。当你走进crawler的世界,你就会发现,其实挺有意思的。最后,希望大家不要面向监狱爬虫,数据千万条,遵纪守法第一条。

Scrapy爬虫及案例剖析的更多相关文章

  1. scrapy爬虫简单案例(简单易懂 适合新手)

    爬取所有的电影名字,类型,时间等信息 1.准备工作 爬取的网页 https://www.ddoutv.com/f/27-1.html 创建项目 win + R 打开cmd输入 scrapy start ...

  2. 爬虫系列2:scrapy项目入门案例分析

    本文从一个基础案例入手,较为详细的分析了scrapy项目的建设过程(在官方文档的基础上做了调整).主要内容如下: 0.准备工作 1.scrapy项目结构 2.编写spider 3.编写item.py ...

  3. Python爬虫——Scrapy整合Selenium案例分析(BOSS直聘)

    概述 本文主要介绍scrapy架构图.组建.工作流程,以及结合selenium boss直聘爬虫案例分析 架构图 组件 Scrapy 引擎(Engine) 引擎负责控制数据流在系统中所有组件中流动,并 ...

  4. scrapy爬虫具体案例步骤详细分析

    scrapy爬虫具体案例详细分析 scrapy,它是一个整合了的爬虫框架, 有着非常健全的管理系统. 而且它也是分布式爬虫, 它的管理体系非常复杂. 但是特别高效.用途广泛,主要用于数据挖掘.检测以及 ...

  5. scrapy爬虫具体案例详细分析

    scrapy爬虫具体案例详细分析 scrapy,它是一个整合了的爬虫框架, 有着非常健全的管理系统. 而且它也是分布式爬虫, 它的管理体系非常复杂. 但是特别高效.用途广泛,主要用于数据挖掘.检测以及 ...

  6. scrapy爬虫,cmd中执行日志中显示了爬取的内容,但是运行时隐藏日志后(运行命令后添加--nolog),就没有输出结果了

    cmd下执行scrapy爬虫程序,不报错也没有输出,解决方案 想要执行parse能够在cmd看到parse函数的执行结果: 解决方法: settings.py 中设置  ROBOTSTXT_OBEY  ...

  7. scrapy爬虫结果插入mysql数据库

    1.通过工具创建数据库scrapy

  8. Python之Scrapy爬虫框架安装及简单使用

    题记:早已听闻python爬虫框架的大名.近些天学习了下其中的Scrapy爬虫框架,将自己理解的跟大家分享.有表述不当之处,望大神们斧正. 一.初窥Scrapy Scrapy是一个为了爬取网站数据,提 ...

  9. Linux搭建Scrapy爬虫集成开发环境

    安装Python 下载地址:http://www.python.org/, Python 有 Python 2 和 Python 3 两个版本, 语法有些区别,ubuntu上自带了python2.7. ...

随机推荐

  1. python中str.isdigit()用法

    str.isdigit()中只包含数字返回true,包含其它则返回false

  2. 消息中心 - Laravel的Redis队列(一)

    前言 Laravel的队列可以用在轻量级的队列需求中.比如我们系统中的短信.邮件等功能,这些功能有一些普遍的特征,异步.重试.并发控制等.Laravel现在主要支持的队列服务有Null.Sync.Da ...

  3. 远程传输命令scp

    Linux scp 命令用于 Linux 之间复制文件和目录. scp 是 secure copy 的缩写, scp 是 linux 系统下基于 ssh 登陆进行安全的远程文件拷贝命令. scp 是加 ...

  4. PHP file_get_contents 读取js脚本的问题

    PHP file_get_contents 读取js脚本的问题 如果文件中带有js脚本 会触发 比方说alert 这个时候 你不用去管他

  5. 【Swift】UNNotificationServiceExtension

    一.简介 An object that modifies the content of a remote notification before it's delivered to the user. ...

  6. 201871010114-李岩松《面向对象程序设计(java)》第十一周学习总结

    项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.com/nwnu-daizh/p ...

  7. tornado的使用-上传图片

    tornado的使用-上传图片

  8. PHP+Swoole并发编程的魅力

    PHP语言是一个短生命周期的Web编程语言,很多PHPer已经形成了fpm下编程的思维定势.实际上在Swoole出现之后,这种串行化编程的模式早已被打破.使用Swoole完全可以轻易实现更灵活的并发编 ...

  9. ThinkPHP v5.1.x POP 链分析

    环境:MacOS 10.13 MAMAP Prophp 7.0.33 + xdebugVisual Studio Code前言我所理解的 POP Chain:利用魔术方法并巧妙构造特殊属性调用一系列函 ...

  10. (二十九)golang--map

    map:是key-value数据结构,又称为字段或者关联数组,类似其它编程语言的集合: 基本语法:var 名称 map[键类型]值类型 key的类型可以是:bool.数字.string.指针.管道,还 ...