爬虫爬 JSON HTML 数据


思否
爬虫 数据采集 Vue

转载自 SegmentFault

最近这两周在忙着给公司爬一点数据,更文的速度有一点下降,预计今天就爬完了,总结总结经验。

其实之前我司是有专门做爬虫的,不用前端这边出人干活。后来那人离职了,有可能就没有爬虫这方面的需求了。突然又有了一些,前端这边出人做一下。老大说用 py 做,前期先调研一下。

原理

爬虫其实原理上很简单,我们==客户端,他们==服务端。 客户端发送请求 req,服务端返回响应 rsp。拿到响应之后解析数据,入库,就完事了。

请求数据 req

一般来说请求分为两种,拉数据 get 比较多。 偶尔部分接口需要登录,那就是多带 cookie 或者 headers。 其实还有一部分工作就是分析入参。

get 参数拼接在 url

post 参数放在 body

响应数据 rsp

返回数据大体上是两种

JSON 一般来说,通过 抓包 或者说 network 工具。我们找到了服务端的接口,那么我直接访问这个接口即可。 本文第一个重点来了:切换到移动端再查一遍,往往有不一样的收获,一般来说 PCM 端的进度不了,有可能都不是一个项目组,所以实现方式就会有差别。

html 比较坑的一种方式,因为没有找到 JSON 接口。无奈只能走解析 HTML 的路子。

调研

Node 之前给后台搭架子的时候使用过,主要功能点如下:

自动登录,(拿headers、cookie

  • 存储本地,每次请求带上 token

  • 启动代理服务

py 老大说要用这个东西。咨询了一下其他朋友,说可以使用下面的工具。

requests + beautifulSoup 使用起来其实就是 requests 发请求, beautifulSoup 解析 HTML。比较原始。

scrapy 一个爬虫的框架。我在这里学的 www.scrapyd.cn。实现上比较完整,可以设置请求间隔,随机 ua 等功能。

前端实现 我一个铁头娃,怎么能轻言放弃?身为一个前端er,还是这些 api 让我更加亲切

XHR 发请求利器,打开对方页面,cookie 啥的都自带。无敌就是这么寂寞。 其实还可以找到对方请求发起的位置,打个断点,把对方内部的代码绑定到全局,这样一些内部逻辑什么的也都没问题。 而且还 JSON HTML 通吃。

iframe 针对 HTML 类型的处理。同域的情况下,也无敌好吗?

HTML 获取 DOM 节点?

  • 甚至可以取 window 上的对象。vue SSR 你感觉到了威胁吗?

网上其他服务商提供的接口(真香啊)。有免费的有收费的,一般免费的限量。

比如抖音热度?

  • 比如各类音乐的歌单和作品?

  • IP 查询

  • 天气查询

好了上面说了那么多,建议老大限制,我选择了 scrapy

scrapy

scrapy 是一个网页爬虫框架,神马叫做爬虫,如果没听说过,那就:内事不知问度娘,外事不决问谷歌,百度或谷歌一下吧!……(这里的省略号代表 scrapy 很牛逼,基本神马都能爬,包括你喜欢的苍老师……这里就不翻译了)

看到这个骚的飞起的介绍了吗?没错,我就是在上面学的。scrapy 中文站。接下来我就介绍一下我认为对于新手比较关注的东西

scrapy HTML

scrapy 处理器中的 response 标识你拿到的 rsp 上面自带了一些方法,一般来说需要关注的只有两个

css 选择器

quote.css('span.text::text').extract_first() 中的 'span.text::text'眼熟吗? 没错,就是我们常用的选择器。通过这个 api,我们可以把我们想要的数据,限时在一个很小的范围,然后拿字符串即可。 啥?你说你不会 css 选择器?前端培训-初级阶段(5 - 8)-CSS选择器(基本、层级、属性、伪类、伪状态)

extract() 函数提取列表

extract_first() 代表提取第一个元素。基本等价于 extract()[0]

::text 选择其中的文字

::attr(href) 提取属性

xpath

quote.xpath('span/small/text()').extract_first() 文档,这个我不会,我也没看

scrapy JSON

import json 使用这个类库解析如:json.loads(response.body.decode('utf-8'))

scrapy 请求方式

get

import urllib 可以用来给中文字符 encode

yield scrapy.FormRequest(
    url, 
    method = 'GET', 
    headers = self.headers, 
    formdata={}, 
    callback = self.parse_list, 
    dont_filter = True, 
    meta = {
        'offset': 0,
    })

post

数据放入 formdata 传递即可。

yield scrapy.FormRequest(
    url, 
    method = 'POST', 
    headers = self.headers, 
    formdata={}, 
    callback = self.parse_list, 
    dont_filter = True, 
    meta = {
        'offset': 0,
    })

给回调模块带参数

meta = {
        'offset': 0,
    }

如下方式接收

disstid = response.meta['offset']

外部传参方式

scrapy crawl argsSpider -a tag=爱情 内部是使用如下命令可以接收到。

    def start_requests(self):
        url = 'http://lab.scrapyd.cn/'
        tag = getattr(self, 'tag', None)  # 获取tag值,也就是爬取时传过来的参数

scrapy mysql

大数据那边说爬回来的数据要入库。

scrapyMysql/scrapyMysql/items.py 编写对应入库字段。

import scrapy

class ScrapymysqlItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    tag = scrapy.Field()  # 标签字段
    cont = scrapy.Field()  # 名言内容
    pass

scrapyMysql/scrapyMysql/spiders/inputMysql.py 写爬虫处理操作时,入库

item = ScrapymysqlItem()  # 实例化item类
    for v in mingyan:  # 循环获取每一条名言里面的:名言内容、作者、标签
        item['cont'] = v.css('.text::text').extract_first()  # 提取名言
        tags = v.css('.tags .tag::text').extract()  # 提取标签
        item['tag'] = ','.join(tags)  # 数组转换为字符串
        yield item  # 把取到的数据提交给pipline处理

编写MySQL存储插件:MySQLPipeline.py

import pymysql.cursors
class MySQLPipeline(object):
    def __init__(self):
        # 连接数据库
        self.connect = pymysql.connect(
            host='127.0.0.1',  # 数据库地址
            port=3306,  # 数据库端口
            db='scrapyMysql',  # 数据库名
            user='root',  # 数据库用户名
            passwd='root',  # 数据库密码
            charset='utf8',  # 编码方式
            use_unicode=True)
        # 通过cursor执行增删查改
        self.cursor = self.connect.cursor()

    def process_item(self, item, spider):
        self.cursor.execute(
            """insert into mingyan(tag, cont)
            value (%s, %s)""",  # 纯属python操作mysql知识,不熟悉请恶补
            (item['tag'],  # item里面定义的字段和表字段对应
             item['cont'],))
        # 提交sql语句
        self.connect.commit()
        return item  # 必须实现返回

settings启动MySQLPipline组件

ITEM_PIPELINES = {
   'scrapyMysql.MySQLPipline.MySQLPipeline': 300,
}

总结一下

到现在,我们已经完成了所有基础知识的积累。遇到不会我们去里看?。 总结一下需要注意点的

  • 切换 PC 和 M 端,寻找可行的方案

  • 注意节制(部分容易限量)

  • python 编码问题(真的好烦)

  • 网上提供的那个 mysql 库和我的不合,我换了一个MySQLdb

  • 第三方的接口是真香

微信公众号


原文:SegmentFault

评论

评论功能即将开放。你也可以通过 邮件GitHub Issue 反馈。