一种无懈可击而又简单的爬虫方案

有时候,我们因为业务发展需要,需要重第三方数据平台获取一些数据,然后可能平台并没有提供比较友好的数据下载方式(可能你没有充值,哈哈),因此我们需要想一些办法来获取这些结构化的数据。

那么,想要达到我们的目的,我们大概有哪几种可能的方式呢?以下是我的一个浅显的思考,大概有以下几种方式可以达到我们的目的:

几种爬取数据的方式

  • HTML信息解析

这种方式举个例子,最直接的理解,就是通过BeautifulSoup等库来解析HTML,从网页中提取你需要的结构化信息,你需要什么信息,自己去写匹配就好了。

ini 复制代码
from bs4 import BeautifulSoup
import requests

# 请求网页
url = "http://example.com"
response = requests.get(url)

# 创建BeautifulSoup对象
soup = BeautifulSoup(response.text, 'html.parser')

# 找到所有的<a>标签
links = soup.find_all('a')

# 提取链接信息
for link in links:
    print(link.get('href'))
  • 使用Python写爬虫

这种一直就是很流行的方式了,写好爬虫几乎全自动化,代表作就是使用Scrapy框架,它提供了所有你需要编写网络爬虫的功能,例如请求处理、数据提取、数据处理和存储等。这里不过多赘述了,当然Python爬虫会比HTML信息解析要完善很多,这个可以自动去访问很多页,然后获取信息。

  • 使用自动化测试框架

可能你没有见过这种方式,但是他的确是一个可行的方式,比如使用playwright自动化测试框架去获取网页的结构化信息。

ini 复制代码
const playwright = require('playwright');

async function scrape() {
    const browser = await playwright["chromium"].launch();
    const context = await browser.newContext();
    const page = await context.newPage();

    await page.goto('http://example.com');

    // 获取所有的<a>标签的href属性
    const links = await page.$$eval('a', links => links.map(link => link.href));

    console.log(links);

    await browser.close();
}

scrape();
  • 浏览器插件

这里我给一个代表作,webscraper[1] ,其背后的原理就是向网页中注入一个content.js,这个content.js干了啥事呢,他干的事情就是去做点击网页,去做结构化信息的收集,然后执行一些其他,然后汇总给到你导出。如图所示:

他的操作界面是chrome开发者工具下面的一个tab页面,你需要在里面配置一些规则,比如告诉他那些是link,哪些是你最终收集的数据字段等等。

api接口?

当然,最后不得不提一个的是,如果网页提供了api接口,你甚至可以直接通过重放和改写api接口请求参数的方式来抓取数据。但是对于靠数据吃饭的网站,你可能不要想得太美了。

几种方式爬取数据的弊端

虽然以上几种方式都具备获取你所需要的结构化信息的能力,但是各有利弊。

HTML结构化信息解析的方式可能只针对单页的情况,这种方式不提也罢,是最低级的方式,Python爬虫的方式相对来讲难度比较适中,但是如果需要处理登录态,处理图形验证等反爬措施时,就显得很难。

而,自动化测试框架也是不太容易处理图形验证码的情形。那么,对于最后的api接口的方式,有存在参数签名,这种方式的限制,哪怕是你改一点点参数,都会请求非法,签名算法通常是保密的,很难被破解。

爬虫还可能会被一种诱捕爬虫的技术所限制,比如网站会设置一些人类用户无法看见或无法访问的陷阱,如果爬虫访问了这些陷阱,那么它就会被识别为爬虫并被阻止。

所以,我们想要轻松拿到 晴来的web上的结构化信息,改何去何从呢?

真正的杀手锏

打开都知道,浏览器请求数据,底层无非是fetch和XMLHttpRequest的方式,因此我们难道不可以代理一下这两个底层的api吗?回答肯定是可以的:

我们说干就干,直接把下面代码复制到chrome console中尝试一下,你就知道效果了。

javascript 复制代码
(function() {
    'use strict';

    // Intercept XMLHttpRequest
    (function(open) {
        XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {
            this.addEventListener('readystatechange', function() {
                if(this.readyState === 4) { // DONE
                    console.log('XHR finished:', method, url, 'status:', this.status);
                    //log response: 
										console.log(this.responseText);
                }
            }, false);
            open.call(this, method, url, async, user, pass);
        };
    })(XMLHttpRequest.prototype.open);

    // Intercept fetch
    (function(fetch) {
        window.fetch = async (...args) => {
            console.log('Fetch called with URL:', args[0]);
            const response = await fetch(...args);
            response.clone().text().then(text => {
                console.log('Fetch response for URL:', args[0], 'Body:', text);
            }).catch(err => console.error('Error reading fetch response:', err));
            return response;
        };
    })(window.fetch);
})();

走出这么突破性的一步,就意味着我们可以捕获所有的请求了,那么,结合一个油猴,你应该就明白了,在web页上增加一个按钮,记录你所需要的数据回包,甚至进一步做一些数据处理,最终返回也不是什么难事了,如图所示:

代理底层XMLHttpRequest的好处

很明显,这种方式不存在之前咱们说的任何一种的弊端,有人可能会说,你这个不是需要手动点击吗,我想说的是,做自动点击难道不是添砖加瓦的事吗?可以说代理底层XMLHttpRequest方案唯一不能满足的场景可能是SSR方式的web,但是就目前前端技术栈来说,SSR通常不大会用于这种数据密集型站点,大多是一些产品主页,文档等。

ini 复制代码
var links = document.querySelectorAll('a.myClass');
for (var i = 0; i < links.length; i++) {
    links[i].click();
}

参考资料

1

webscraper:

www.webscraper.io/documentati...

探索代码的无限可能,与老码小张一起开启技术之旅。点关注,未来已来,每一步深入都不孤单。

相关推荐
Arva .4 分钟前
电子书查询列表接口开发
后端
道可到6 分钟前
写了这么多代码,你真的在进步吗??—一个前端人的反思与全栈突围路线
前端
间彧6 分钟前
在Spring Cloud Gateway中,如何自定义GlobalFilter实现动态路由规则?
后端
间彧7 分钟前
在微服务架构中,过滤器与网关(如Spring Cloud Gateway)如何配合使用?
后端
洛克大航海7 分钟前
Ajax基本使用
java·javascript·ajax·okhttp
用户9163574409510 分钟前
LeetCode热题100——11.盛最多水的容器
javascript·算法
间彧11 分钟前
SpringBoot过滤器详解与项目实战
后端
武子康18 分钟前
大数据-121 - Flink 时间语义详解:EventTime、ProcessingTime、IngestionTime 与 Watermark机制全解析
大数据·后端·flink
两万五千个小时24 分钟前
LangChain 入门教程:构建你的第一个聊天机器人
后端
凡大来啦25 分钟前
v-for渲染的元素上使用ref
前端·javascript·vue.js