登录
转载

pyhon爬虫—爬取原创力文档(全面解析)

发布于 2021-04-02 阅读 4502
  • 后端
  • Python
转载

目录

  • 前言
  • 环境准备
  • 一、分析网页结构
    • 判断网页为静态还是动态
  • 二、编写代码
    • 请求网址,解析数据
    • 循环获取全部数据
    • 下载文档图片
  • 三、优化代码
    • 目的
    • 实现过程
    • 补充
  • 小结

前言

寒假时收到了一个小任务,在百度上下载一些“规范文档”。阴差阳错下,找到了原创力文档这个网站,在里面我找到了所需的全部文档。但是,因为在网站内下载文档需要点小代价,所以我写下了这个爬虫。在此分享一下。(注:仅供学习参考)

环境准备

编程语言:Python3.7 IDE:Pycharm 浏览器:Google Chrome

一、分析网页结构

原创力文档首页网址:https://max.book118.com/ 图1 网站首页陈列了部分精选文档,也可通过在搜索框查询所需文档。为方便演示爬虫的效果,我挑选了一篇24页的文档。

文档名称:LIMS应用与认可要求 文档网址https://max.book118.com/html/2021/0328/6155232140003130.shtm 图2

判断网页为静态还是动态

图3 如果该网页为静态网页,通过Google浏览器我们可以很轻松的查找到文档图片的网址,再使用xpath将文档图片悉数爬下。

然而在查看网页源代时,并没有查询到该网址。 图4 所以很明显,网页为动态网页。

通过浏览器的抓包功能,我在Network中找到一个名为“getPreview.html……”的请求。查看Preview,可以发现里边就是文档前6页图片的网址。 图5 也就是说,此次爬取的目标信息就在Headers中。 图6

二、编写代码

请求网址,解析数据

代码:

import requests

headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36'}
url = 'https://openapi.book118.com/getPreview.html'
params = { # 参数(只保留了关键的参数)
    'project_id': '1',
    'aid': '354607192',
    'view_token': 'HjXCVKRHT4l_IQyz9cnzs9nffOPCKQPQ',
    'page': '1',
}
response = requests.get(url=url, headers=headers, params=params).text
print(response)

输出结果: 图7 可以发现,返回的是一组json格式的数据,但是因为字符串“jsonpRrturn()”的存在,无法直接使用json.loads()函数对其进行转换。所以我用正则表达式提取了关键数据后,再进行转换。

转换结果: 图8 转换成json格式数据后,通过key将数据遍历出来,即可获得所需的文档图片网址。

代码:

response_json = re.search('jsonpReturn\((.*?)\);', response).group(1)  # 使用正则表达式所需数据
data = json.loads(response_json)['data']
for i in data.items():  # i[0]为页数,i[1]为网址
    img_url = 'https:' + i[1]
    print(i[0], img_url)

输出结果: 图9

循环获取全部数据

目前只获取了文档前6页的内容,而剩下的内容通过修改参数来获取便可。

点击需预览,获取剩下内容的请求。 图10 图11 对比两组参数,很容易发现,page发生了变化。

既然1次请求可以获取6页内容,那也就是说24页的内容,4次请求便可以获取全部内容了。并由此推断page的变化情况为1、7、13、19 。使用for循环实现即可。

出现的问题:爬取第六页后的结果全部为空。 图12 为了寻找原因,我单独请求page为7时的内容。发现数据正常的获取到了,推断是请求速度过快,数据还未加载。 图13 解决方案:使用time.sleep()函数,延迟请求速度。

代码:

for page in range(1, 20, 6):
    params = {
        'project_id': '1',
        'aid': '354607192',
        'view_token': 'HjXCVKRHT4l_IQyz9cnzs9nffOPCKQPQ',
        'page': page,  # page的变化情况为1、7、13、19
    }
    response = requests.get(url=url, headers=headers, params=params).text
    response_json = re.search('jsonpReturn\((.*?)\);', response).group(1)  # 使用正则表达式所需数据
    data = json.loads(response_json)['data']
    for i in data.items():  # i[0]为页数,i[1]为网址
        img_url = 'https:' + i[1]
        print(i[0], img_url)
    time.sleep(5)

输出结果: 图14

下载文档图片

代码:

urllib.request.urlretrieve(url=img_url, filename=r'D:/yuanChuangLi/{}.png'.format(i[0]))

输出结果: 图15 通过一些工具,可将所爬取到的图片转换为PDF格式。这里便不多介绍。

三、优化代码

目的

1)实现输入文档的网址,即可爬取文档内容。 2)增强代码可读性。

实现过程

通过对比不同文档可以发现,不同文档的参数 aid、view_token 会发生改变。那通过修改aid、view_token不就可以获取不同文档内容了吗? 图16 然后我在网页源代码中搜索aid,果然,文档的aid、view_token都在网页源代码里,并且文档的页数也在其中。 图17 那么,接下来便可以对文档的网址进行请求,再通过正则表达式获取到文档的aid、view_token和页数了。

代码:

def getParameter():
    text_url = input('输入网址:')
    text_response = requests.get(url=text_url, headers=headers).text
    actual_page = int(re.search('actual_page: (\d+), //真实页数', text_response).group(1))
    aid = re.search('aid: (\d+), //解密后的id', text_response).group(1)
    view_token = re.search('view_token: \'(.*?)\'', text_response).group(1)
    return actual_page, aid, view_token

输出结果: 图18 利用函数调用的方式整理代码,获得最后成果

完整代码:

import requests, json, re, time, urllib.request


def getParameter(url):  # 获取文档参数
    text_response = requests.get(url=url, headers=headers).text
    actual_page = int(re.search('actual_page: (\d+), //真实页数', text_response).group(1))  # 页数
    aid = re.search('aid: (\d+), //解密后的id', text_response).group(1)  # aid
    view_token = re.search('view_token: \'(.*?)\'', text_response).group(1)  # view_token
    print('actual_page:', actual_page, '\naid:', aid, '\nview_token:', view_token)
    return actual_page, aid, view_token


def requests_data(parameter, page):  # 请求数据
    url = 'https://openapi.book118.com/getPreview.html'
    params = {
        'project_id': '1',
        'aid': parameter[1],
        'view_token': parameter[2],
        'page': page,
    }
    response = requests.get(url=url, headers=headers, params=params).text
    json_data = re.search('jsonpReturn\((.*?)\);', response).group(1)  # 使用正则表达式所需数据
    data = json.loads(json_data)['data']
    # if data.get(str(page)) == '':  # 根据需求使用
    #     print('数据加载失败,重新发出请求')
    #     time.sleep(1)
    #     return requests_data(parameter, page)
    # time.sleep(1)
    return get_data(data)


def get_data(data):  # 下载数据
    for i in data.items():  # i[0]为页数,i[1]为网址
        img_url = 'https:' + i[1]
        # urllib.request.urlretrieve(url=img_url, filename=r'D:/yuanChuangLi/{}.png'.format(i[0]))  # 下载图片
        print(i[0], img_url)


if __name__ == '__main__':
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36'}
    text_url = input('输入网址:')
    parameter = getParameter(text_url)
    for page in range(1, parameter[0], 6):
        requests_data(parameter, page)

补充

因为下载图片需要一定的时间,这点时间足够让数据加载出来,这时前面添加的time.sleep()函数就显得鸡肋了。 但是,为了让代码能有更高的容错。我在requests_data模块中添加了以下代码(已在完整代码中):

if data.get(str(page)) == '': 
    print('数据加载失败,重新发出请求')
    time.sleep(1)
    return requests_data(parameter, page)

其功能是在请求失败时,利用递归的方式,对请求失败的网址再次发出请求,直到请求成功为止。

输出结果: 图19

小结

1)经过测试,原创力文档中有小部分为VIP文档,无法爬取。 2)这个爬虫所需技术要求不高,但也算次不错的实操。小伙伴们有兴趣可以练习、扩展一下。

第一次写博客,有很多不足,请多多见谅 谢谢观看。

评论区

我是搬运工2号,没人自称1号

0

0

0

举报