爬虫第三天:执行JS脚本

解析执行 JS 脚本代码,获取数据信息

前一篇 讲解了如何使用 XPath 解析 HTMLDOM 元素。要想拿到全部的博客文章,前提是拿取到页面的分页信息;如今面临的问题是如何获取博客的分页信息???

通过浏览器 F12 -> Network 中查看我们的爬取的页面 ‘https://blog.csdn.net/menglinjie’ 返回的信息,发现其中并没有页码信息,如下图所示,

<div class="pagination-box" id="pageBox"></div>

此DIV标签内没有任务东西,可得出结论:页面所呈现的分页信息是由JS代码动态渲染上的
F12 URL响应
通过继续查询该URL的响应信息,不难发现存在一段这样的代码:
分页脚本信息
很明显是该博主博客的分页数据:

    var currentPage = 1; // 当前页码 
    var baseUrl = 'https://blog.csdn.net/menglinjie/article/list' ; // 分页查询URL
    var pageSize = 40 ; // 分页大小
    var listTotal = 127 ; // 博客文章总数
    var pageQueryStr = ''; // 查询参数
    /**
    * 获取完整的分页查询URL
    */
    function getAllUrl(page) {
        return baseUrl + "/" + page + pageQueryStr;
    }

分页信息已经呈现我们面前,但是此处已经不能通过Xpath解析了,摆在面前的首要问题就是如何解析JS脚本?

So:

这里需要引入一个可以调用 JS 脚本的 Python 库: PyExecJS

1. 引入 PyExecJS

1.1 项目导入PyExecJS

命令执行

pin install PyExecJS

代码导入该模块:

import execjs

1.2 PyExecJS使用介绍

查看模块下 __init__.py 源码:

.....

register = execjs._runtimes.register
get = execjs._runtimes.get
runtimes = execjs._runtimes.runtimes
get_from_environment = execjs._runtimes.get_from_environment


def eval(source, cwd=None):
    return get().eval(source, cwd)
eval.__doc__ = AbstractRuntime.eval.__doc__


def exec_(source, cwd=None):
    return get().exec_(source, cwd)
exec_.__doc__ = AbstractRuntime.exec_.__doc__


def compile(source, cwd=None):
    return get().compile(source, cwd)
compile.__doc__ = AbstractRuntime.compile.__doc__

提供了三个方法:eval, exec_, compile

  • exec_: 通过JS运行环境执行JS源代码并将输出值作为 String字符串返回
    • 例:print(execjs.exec_(‘return 1’)) # 1
    • 例:print(execjs.exec_(‘1 + 1’)) # None
  • eval: 在JS运行环境执行JS表达式
    • 例:print(execjs.eval(‘return 1’)) # 报错
    • 例:print(execjs.eval(‘1 + 1’)) # 2
  • compile: 作为上下文对象的源。源代码可用于执行其他代码

2. 应用

再回顾一下我们需要解析的 JS 代码:

    var currentPage = 1; // 当前页码 
    var baseUrl = 'https://blog.csdn.net/menglinjie/article/list' ; // 分页查询URL
    var pageSize = 40 ; // 分页大小
    var listTotal = 127 ; // 博客文章总数
    var pageQueryStr = ''; // 查询参数
    /**
    * 获取完整的分页查询URL
    */
    function getAllUrl(page) {
        return baseUrl + "/" + page + pageQueryStr;
    }

我们需要的数据:

  • pageSize: 分页大小
  • listTotal:博客总数

由此两值即可计算出分页情况。

所以:

第一步:先编写我们的JS片段

// 获取总数
function getTotalCount(){
    return listTotal;
}
// 获取分页大小
function getPageSize(){
    return pageSize ;
}

第二步:与源JS代码一起编译进上下文

# script变量为源JS代码
# custom_script变量为上述自定义的JS片段
execjs_compile = execjs.compile(str(script) + custom_script)

第三步:执行JS脚本,获取方法返回值
execjs.compile 返回 ExternalRuntime.Context 对象,其拥有 def call(self, fn, *args, **kwargs) 方法

fn: 方法名
args:方法参数

# 执行 getTotalCount 方法
list_total = execjs_compile.call('getTotalCount')
# 执行 getPageSize 方法
page_size = execjs_compile.call('getPageSize')

3. 总结

完整python代码:

# 通过 xpath 获取 html中 script 标签内容
path_xpath = '//script/text()'
response_html = httpUtil.Request(url=blog_url).get_response()
html = etree.HTML(response_html)
# 解析得到所有的 script 脚本内容
path_html = html.xpath(path_xpath)
if len(path_html) == 0:
    print('无分页信息!')
    return
for script in path_html:
    # 遍历找到含有分页信息的脚本片段
    if 'listTotal' in str(script):
        # 自定义脚本
        custom_script = '''
        function getTotalCount(){
            return listTotal;
        }
        function getCurrentPage(){
            return currentPage;
        }
        function getPageSize(){
            return pageSize ;
        }
        '''
        # 编译JS代码加入JS运行环境上下文
        execjs_compile = execjs.compile(str(script) + custom_script)
        # 执行JS方法,获取分页信息
        list_total = execjs_compile.call('getTotalCount')
        page_size = execjs_compile.call('getPageSize')
        print('总文章数量:', list_total)
        print('分页大小:', page_size)
  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值