爬虫新思路[1]-Hook方法爬取数据

最近因为开始有各种各样的爬虫需求,因为本身对写爬虫这事很感兴趣,所以都接下来了,因为会遇到各种各样的反爬的处理所以在隐藏原网站的基本信息来记录每一次爬取的思路和解决方案

这篇就是遇到的其中一个案例,这个案例的难点如下:

  • 网站端用了 webpack 的打包方式
  • 请求的返回结果进行了加密
  • 每个请求需要计算 Sign

第一次做这个爬虫的思路就是通过 webpack的逆向来获取到解密的方式和 sign 加密的方式,但是对webpack 还不太熟悉,在扣了一晚上的代码最终没能执行起来花了一小时也没能排查到问题,则放弃这个想法,来到了这个文章所用的方式:

Hook Js里面的 JSON.parse 方法拦截结果。

想到这个思路的原因,也是搜寻解决方案时看到别人利用这个方式逆向到解密的位置,因为返回结果进行了加密,返回结果是 JSON 的话最后会需要调用这个函数转换成 JS 对象再进行操作。

const originJson = JSON.parse;
JSON.parse = function (text, reviver) {
    const result = originJson(text, reviver);
    console.info(JSON.stringify({"title": document.title, "response":result}))
    return result;
}

那么拦截也需要有个载体去执行,1.可以使用油猴,2.可以用 Selenium / Playwright 载入程序

这次用例爬取的是一个影视网站,获取到每个视频里面的详细信息,因为使用这个方法爬虫的同时也需要浏览器进行翻页、点击视频的操作,所以我选择的方案 2,那么快速的编写一个Selenium 程序操作爬取一下

在编写程序前的流程如下:

启动浏览器->浏览器植入JS->浏览器模拟访问列表->访问视频->点击视频进入详情页->等待接口拦截完毕->回到访问列表进行下一个操作

流程代码如下:

hook_path = "hook.js" # JS HOOK放进去
with open(hook_path) as f:
    hook_path = f.read()

chrome_options = webdriver.ChromeOptions()
chrome_options.enable_bidi = True
chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
chrome_options.add_experimental_option('useAutomationExtension', False)
chrome_options.add_argument('lang=zh-CN,zh')
chrome_options.add_argument('user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36')
chrome_options.add_argument("--disable-blink-features=AutomationControlled") #就是这一行告诉chrome去掉了webdriver痕迹
self.driver = webdriver.Chrome(options=chrome_options)
self.driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {"source": hook_path}) #伪装JS
self.driver.script.add_console_message_handler(lambda msg: write_log(msg)) #转发到写入LOG
self.driver.get("") #访问页面触发hook

运行后需要注意的是:

因为拦截的是JSON,需要事先对 JSON 的数据进行 人工识别判断,根据返回的对象特征找到自己需要的数据特征判断好,如下:


def write_log(msg):
    try:
        for arg in msg.args:
            if 'type' in arg and arg['type'] == 'string':
                msg_dict = json.loads(arg['value'])
                if 'title' in msg_dict and msg_dict['response']:
                    if 'data' in msg_dict['response'] and 'chapter_lists' in msg_dict['response']['data']:
                        with open('shorts/%s.json' % msg_dict['title'], 'w') as f:
                            print("写入data")
                            json.dump(msg_dict['response']['data'], f)
    except Exception as e:
        return False #无关紧要的打印

最后让 Cursor 帮忙生成出一个 JSON转CSV的函数,这个爬虫就写好,丢在另外台机器执行就完事了。

完整示例就不贴出了,主要代码块都如上了。

站点助手

你好!我是站点助手,你可以向我询问站点内的任何信息。