likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 3

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

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

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

几种爬取数据的方式

  • HTML信息解析

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

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自动化测试框架去获取网页的结构化信息。

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中尝试一下,你就知道效果了。

(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通常不大会用于这种数据密集型站点,大多是一些产品主页,文档等。

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

参考资料

[1]

webscraper:

www.webscraper.io/documentati…

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

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