V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
RoverVan
V2EX  ›  程序员

Run JS While Grabing Web Page With PHP(用 PHP 爬取需要运行 JS 的页面)

  •  
  •   RoverVan · 2017-01-08 19:37:30 +08:00 · 1929 次点击
    这是一个创建于 2868 天前的主题,其中的信息可能已经有所发展或是发生改变。

    初衷

    近日在学习爬虫的时候遇到一个小问题,当在抓取某些网页的时候,在线测试通过的正则匹配在用 PHP 抓取时却发现只能抓取某些非关键元素。 经过排查,才发现在抓取该页面(是一个电商页面)时,该页面的详情页面是通过 JS 二次请求动态添加上去的,而 PHP(通过 curl 函数库的方式)只是将其静态页面抓下,所以正则匹配的不是整个渲染好的完整页面,而是一个隐藏了详情板块的页面。

    解决方案

    大致涉猎了一下,一般业界的解决方法有二:

    1. 分析 JS 文件,模拟 JS 中的请求

    2. 想方法运行 JS ,抓取 JS 运行渲染完毕后的页面(本文讲述的方法)

    phantomjs

    file

    phantomjs 基于 WebKit 、开源的服务器端 JavaScript API, 采用了 WebKit 内核的 phantomjs 可以模拟浏览器运行网页,可以浅显的把它理解为除了把访问的页面显示出来。 除此之外,其他浏览器具备的功能它都有了(DOM handling, CSS selector, JSON, Canvas, and SVG),所以可以通过调用它来运行含有 JS 文件且需要运行的 html 页面,当然它的用处肯定不止这些, web 测试,页面截图,网络监控等等(详见官网文档)。

    解决步骤

    Step.1 下载(编译)phantomjs 文件

    这里有两种方式:

    1. 直接从官网下载对应系统编译好的可执行文件,解压后移动到 bin 目录下即可

    2. 官方 Github下载源码后编译为可执行文件。

    我这里向大家介绍比较通用的法一: 如图在官网下载对应你服务器系统的版本,

    file

    以 CentOS 为例,下载 Linux 64-bit 版本(32/64 区分好)

    curl -O https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2
    

    解压文件

    tar xvf phantomjs-2.1.1-linux-x86_64.tar.bz2
    

    移动文件到 bin 目录下

    cp phantomjs-2.1.1-linux-x86_64/bin/phantomjs /usr/local/bin
    

    到这里就 phantomjs 的 install 过程已完成,不过最好测试一下能否成功运行。 随意写一个测试的 js 文件,运行下看是否成功

    phantomjs helloworld.js
    

    若是不成功,按提示安装缺失的 libraries 后再运行。 若还是不行,可以尝试用法二来获取 phantomjs 文件。

    Step.2 通过 PHP 调用 phantomjs

    正常来说到这里的话,我们应该先用 PHP 获取到对应页面的 URL ,然后用 phantomjs 执行后,获取返回的内容,再对其进行正则匹配(替代了原来的 curl 操作)。 我在 Github 发现了有朋友已经封装了一个基于PHP-phantomjs的包,还写了非常健全的文档,已为他献上 Star 。

    file

    由于文档是全英,我这里简单的介绍下关键步骤

    1.通过 Composer 安装

    composer require "jonnyw/php-phantomjs:4.*"
    

    2.初始化 JonnyW\PhantomJs\Client 类

    $client = Client::getInstance();
    //这一步非常重要,务必跟服务器的 phantomjs 文件路径一致
    $client->getEngine()->setPath('/usr/local/bin/phantomjs');
    

    3.简单的使用

    $request  = $client->getMessageFactory()->createRequest();
    $response = $client->getMessageFactory()->createResponse();
    
    //设置请求方法
    $request->setMethod('GET');
    //设置请求连接
    $request->setUrl($link);
    //发送请求获取响应
    $client->send($request, $response);
    
    if($response->getStatus() === 200) {
        //输出抓取内容
        echo $response->getContent();
        //获取内容后的处理
    }
    

    4.加载完整 JS 的用法

    $client = Client::getInstance();
    $client->isLazy(); // 让客户端等待所有资源加载完毕
    
    $request = $client->getMessageFactory()->createRequest();
    $request->setTimeout(5000); // 设置超时时间(超过这个时间停止加载并渲染输出画面)
    
    ......
    

    总结

    最近在看《数学之美》的时候吴军博士在“图论和网络爬虫”一章中提过,如今的网页很多是用 Javascript 生成,在面对这些网页时,网络爬虫需要模拟浏览器去运行。 我也是在看完这一章后对这个点有所印象,这次遇到类似问题就朝这个方向去解决了。希望能给大家带来一点帮助和启发。

    Contact me

    如果有什么错误或者建议 OR 如果需要请教关于本主题的相关问题 欢迎来邮与我交流和讨论!

    Email :[email protected]

    & My blog: ROVERVAN

    2 条回复    2018-02-01 10:13:22 +08:00
    batnss
        1
    batnss  
       2017-01-11 22:13:12 +08:00 via Android
    支持下
    lohiecan
        2
    lohiecan  
       2018-02-01 10:13:22 +08:00
    jsrun 发来贺电: 做的不错啊。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2745 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 05:35 · PVG 13:35 · LAX 21:35 · JFK 00:35
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.