用 Lighthouse 进行入门级的网页抓取

网页内容抓取是指通过网页抓取工具对指定网页进行设定行为的自动访问,并进行数据分析提取、最终持久化至电子表格/数据库等存储的过程。

0x00 概述

网页内容抓取(Web Scraping)是指通过网页抓取工具(即Web Crawler,亦称网页爬虫)对指定网页进行设定行为的自动访问,并进行数据分析提取、最终持久化至电子表格/数据库等存储的过程。此类工作对于科学研究、推荐系统设计、大数据挖掘分析、人工智能、商业分析等多类应用领域都是不可或缺的关键步骤。

本文是一篇入门教程,将向读者介绍网页抓取基本原理和步骤,并基于腾讯云的轻量应用服务器和Python工具Scrapy,快速上手并实践相对简易的爬虫工具。

目标读者:有一定Python实践和Web基础概念的的研究分析人员或技术爱好者。

实践目标:通过代码自动化抓取腾讯视频主页下的部分电影信息,并以CSV形式保存成电子表格。

用 Lighthouse 进行入门级的网页抓取

抓取后存储为CSV,方便电子表格软件展示和进一步处理。

用 Lighthouse 进行入门级的网页抓取

0x01 环境准备

1、云服务器准备

第一步当然是准备环境,云服务器所提供的计算资源和网络能力是网页抓取任务的基础。不过这次让我们来点新鲜的,不用大家已经熟悉的CVM,而是试用下腾讯云新推出的轻量应用服务器,官网称它是最佳入门途径:

轻量应用服务器(Lighthouse)是一种易于使用和管理、适合承载轻量级业务负载的云服务器,能帮助个人和企业在云端快速构建网站、博客、电商、论坛等各类应用以及开发测试环境,并提供应用部署、配置和管理的全流程一站式服务,极大提升构建应用的体验,是您使用腾讯云的最佳入门途径。

这里使用Lighthouse实例的原因无他,主要是配置方便启动快,省得折腾工夫,价格也便宜些。我们直接在控制台新建即可,购买页设计得很简洁:

用 Lighthouse 进行入门级的网页抓取

轻量应用服务器还支持不同的应用镜像,如WordPress、Node.js等,需要的话还是挺方便的。不过本实验用不到,这里我们直接选Ubuntu18.04的系统镜像就足够。登录后根据个人习惯简单配置下开始下一步。

2、Python3 VirtualEnv环境准备

Ubuntu18.04是默认安装了Python3 (3.6.9),但是没有安装对应版本的VirtualEnv。如下命令安装即可:

sudo apt install python3-venv

然后初始化后续项目的venv

python3 -m venv scrapy_examples
cd scrapy_examples
source bin/activate

注意source后,默认的python就是python3了,并且在venv环境中还有了pip,虚拟环境(venv)中的一切是与外界(系统python和相应的库)完全隔离的。

接下来安装scrapy等依赖包,并创建项目:

pip install scrapy
scrapy startproject movie
cd movie

至此准备工作完成。

scrapy创建的movie项目的目录结构如tree命令所示,大部分是框架的配置文件:

(scrapy_examples) ➜  movie tree
.
├── movie
│   ├── __init__.py
│   ├── items.py
│   ├── middlewares.py
│   ├── pipelines.py
│   ├── __pycache__
│   ├── settings.py
│   └── spiders
│       ├── __init__.py
│       └── __pycache__
└── scrapy.cfg

Scrapy既然是框架(Framework),意味着它将作为程序启动的入口,而我们使用者只需实现业务需要的类及其方法即可。

0x02 示例一:静态URL抓取

我们从一个最简单的固定URL列表访问的示例开始。

新建文件 movie/spiders/movie_spider_1.py,代码内容如下:

import scrapy

class MovieSpider1(scrapy.Spider):
    name = "movie_1"

    def start_requests(self):
        urls = [
            'https://v.qq.com/channel/movie?listpage=1&channel=movie&itype=100012', # 科幻
            'https://v.qq.com/channel/movie?listpage=1&channel=movie&itype=100005', # 爱情
            'https://v.qq.com/channel/movie?listpage=1&channel=movie&itype=100004', # 喜剧
        ]
        for url in urls:
            yield scrapy.Request(url=url, callback=self.parse)

    def parse(self, response):
        movie_list = response.css('body > div.mod_row_box > div > div.mod_figure.mod_figure_v_default.mod_figure_list_box > div')
        for m in movie_list:
            yield {
                '电影标题': m.css('a::attr(title)').get(),
                '播放地址': m.css('a::attr(href)').get(),
                '海报链接': 'https:{}'.format(m.css('a > img.figure_pic::attr(src)').get()),
                '播放时长': m.css('a > div.figure_caption::text').get(),
                '影评分数': m.css('a > div.figure_score::text').get(),
            }

运行crawl子命令执行网页抓取任务:

scrapy crawl movie_1 -o movies.csv

指定名称为movie_1的spider,注意这里须要MovieSpider1类的属性name一致,并将结果输出成csv。另外scrapy同时还支持json/jsonlines/xml等多种格式。输出大致如下:

(scrapy_examples) ➜  movie head movies.csv 
电影标题,播放地址,海报链接,播放时长,影评分数
僵尸世界大战,https://v.qq.com/x/cover/r5trbf8xs5uwok1.html,https://puui.qpic.cn/vcover_vt_pic/0/r5trbf8xs5uwok11590989398026/220,02:03:02,7.7 
复仇者联盟4:终局之战,https://v.qq.com/x/cover/v2098lbuihuqs11.html,https://puui.qpic.cn/vcover_vt_pic/0/v2098lbuihuqs111587100715029/220,02:54:05,9.2 
美人鱼,https://v.qq.com/x/cover/xg95sxi4q7zc4uo.html,https://puui.qpic.cn/vcover_vt_pic/0/xg95sxi4q7zc4uot1460107848.jpg/220,01:33:45,8.3 
环太平洋:雷霆再起,https://v.qq.com/x/cover/f7pqur8uhmzltps.html,https://puui.qpic.cn/vcover_vt_pic/0/f7pqur8uhmzltps1559809738/220,01:50:59,7.6 
钢铁飞龙之奥特曼崛起,https://v.qq.com/x/cover/380idj4s3fxn1mz.html,https://puui.qpic.cn/vcover_vt_pic/0/380idj4s3fxn1mz1543994759/220,01:30:43,6.5 
地心历险记2:神秘岛,https://v.qq.com/x/cover/ny2pykcofj3tljs.html,https://puui.qpic.cn/vcover_vt_pic/0/ny2pykcofj3tljs1567405217/220,01:34:10,8.1 
速度与激情:特别行动,https://v.qq.com/x/cover/mu01sbah4rauf44.html,https://puui.qpic.cn/vcover_vt_pic/0/mu01sbah4rauf441573116092/220,02:13:52,8 
透明人,https://v.qq.com/x/cover/l3u3m6tzeddnnhk.html,https://puui.qpic.cn/vcover_vt_pic/0/l3u3m6tzeddnnhkt1444906881.jpg/220,01:46:34,8.2 
星际大逃亡,https://v.qq.com/x/cover/mzc00200a20krmb.html,https://puui.qpic.cn/vcover_vt_pic/0/mzc00200a20krmb1590481722077/220,01:28:25,7.7 

上述并没有什么黑魔法,这里简要解释一下重点:

这个继承自scrapy.Spider类的MovieSpider1类,是用来定义整个的内容抓取逻辑的。

网页内容抓取核心的三个问题就是:

  • Request 请求哪些网页,以及请求的逻辑条件:该例通过start_requests方法定义了初始请求的url列表,即3个静态的网页URL。而每个请求,其成功后都会执行指定的回调函数来完成后续的解析工作,如parse函数。通过python代码理论上可以实现任意复杂的动态请求逻辑。
  • Parse 如何解析提取信息:通过选择器(selector)来完成,相对简单通用的CSS选择器外,还支持XPATH等更高级用于复杂解析。
  • Store 存在哪里:该例通过parse回调函数的yield返回结果,通过框架将其存于csv文件。

仔细厘清以上三点的逻辑,是编写spider类的重点。

Tips如果对CSS选择器的语法不那么熟悉怎么办?

当然可以去从这里这里进行系统复习,不过以下方法更加方便。

Chrome浏览器开发工具的元素审查(Inspect)功能可以快速定位DOM结构,选中对应的节点,右键复制菜单里有Copy Selector,直接可以导出CSS选择器的表达式,如下图:

用 Lighthouse 进行入门级的网页抓取

复制出的表达式为如下

body > div.mod_row_box > div > div.mod_figure.mod_figure_v_default.mod_figure_list_box > div:nth-child(1)

用它稍作改动就可以用于代码中的解析步骤了,轻松~

0x03 示例二:动态URL抓取

示例一的方法仅能抓取首屏渲染的部分电影信息,即每个子类仅是最靠前的30个电影,而相对排名靠后的电影是需要手动滚动才能出发动态的数据按需拉取。那么如何抓取某一分类下的全部电影呢?

其实该站点有更加“爬虫友好”的页面以方便自动化访问,拉取数据的是通过URL参数中的query_string参数来实现分页的,所以我们可以通过动态调整请求来实现全部抓取全部数据,或者说动态的决策请求的URL。

新建文件 movie/spiders/movie_spider_2.py,代码内容如下:

import scrapy
from urllib.parse import parse_qs

class MoviesSpider2(scrapy.Spider):
    name = "movie_2"
    # download_delay = 1.0

    base_url_template = 'https://v.qq.com/x/bu/pagesheet/list?append=2&channel=movie&itype=100012&listpage=1&pagesize={}&offset={}'
    page_size = 30

    def start_requests(self):
        url = self.base_url_template.format(self.page_size, 0)
        yield scrapy.Request(url=url, callback=self.parse)

    def parse(self, response):
        movie_list = response.css('body > div > div > div.mod_figure.mod_figure_v_default.mod_figure_list_box > div')
        for m in movie_list:
            yield {
                '电影标题': m.css('a::attr(title)').get(),
                '播放地址': m.css('a::attr(href)').get(),
                '海报链接': 'https:{}'.format(m.css('a > img.figure_pic::attr(src)').get()),
                '播放时长': m.css('a > div.figure_caption::text').get(),
                '影评分数': m.css('a > div.figure_score::text').get(),
            }
        # 如果还有,更新url参数继续请求
        if len(movie_list) > 0:
            current_offset = int(parse_qs(response.url)['offset'][0])
            next_url = self.base_url_template.format(self.page_size, current_offset + self.page_size)
            yield scrapy.Request(next_url, callback=self.parse)

以上的逻辑大致是每次请求30个(这是官方支持的最大数)电影信息,直到完结。注意下一个请求的创建依赖当前请求结果的解析,故对于此例(单一初始请求URL),所有的请求是串行的,时间会稍长。

另外注意,以dowload_delay属性可以在请求之间设置延迟,这样可以调整控制访问频次。

用 Lighthouse 进行入门级的网页抓取

不出所料地,目前700+个科幻电影的相关信息就全部摘录完成~

0x04 小结

到了这里,你应该已经可以在腾讯云轻量应用服务器之上,初步搭建属于自己的初级机器爬虫了,开始高效地探索互联网吧!

网页抓取技术所涉及的是一个系统级的工程,从爬虫的逻辑设计、架构性能优化、到安全稳定可扩展等多个方面都会有很多的难点值得深入研究和攻克。Scrapy框架中的各个组件也做了不少优化和组合。

用 Lighthouse 进行入门级的网页抓取

如果觉得有兴趣或收获,点赞评论并期待后续更多干货吧~

P.S. 不要天真地以为抓到播放链接就可以为所欲为,该看的广告还得看完,该付费的首播/独播内容还得氪金,我先去充会员了,毕竟这么多好片子呢。

0x05 参考

  1. 轻量应用服务器(Lighthouse)
  2. Scrapy.org用户文档
  3. 腾讯视频电影精选
  4. develop-your-first-web-crawler-in-python-scrapy

本文来自腾讯云计算社区,转载请注明出处:https://computeinit.com/archives/2596

发表评论

登录后才能评论
交流群