爬虫爬取网站访问量_showDynClicks
一、背景
在爬取某个网站的信息时发现网站的访问量无法获取到,如下图所示:
可以看到阅读次数后面并不是一个数字,而是一个script。
但使用浏览器浏览网页的时候发现是能正常显示(url为http://www.dxsjs.sdu.edu.cn/info/1003/1913.htm),如下图所示:
可见,这个阅读次数2817并非初次访问网站时就能获取到的,而是后续渲染出来的。
所以,直接处理爬取到的html不能解决我们的问题,我们需要去看看这个2817到底是怎么来的。
打开开发者工具,刷新页面,在Fetch/XHR下面我们发现(好吧这会变成2818了):
可见这个阅读次数是异步请求到的,接下来我们就开始设法获取到这个数据。
二、思路
在多次刷新页面后,可以得出结论,对http://www.dxsjs.sdu.edu.cn/system/resource/code/news/click/dynclicks.jsp?clickid=1913&owner=1396647413&clicktype=wbnews
发出GET请求即可获取到数据,其中clickid和owner两个参数是变化的,clicktype是不变的。
而前两个参数的值恰恰就是前面看到的<script>中的两个值。
所以思路很明确了:先获取这个script,得到clickid和owner,然后据此对上面的url发送GET请求获取访问次数。
三、解法
1. 获取整个文档
base_url = 'http://www.dxsjs.sdu.edu.cn/'
def get_html(url):
url = base_url + url
response = requests.get(url)
# 显式指定编码
response.encoding = 'utf-8'
return response.text
这段代码是获取整个html文档的,这里特别地显式指定编码为utf-8,如果不指定的话会出现乱码,这个需要据网站特性而定,不同网站可能编码不同。
2. 获取标签
以下是需要处理的html片段:
<div id="view_info">
发布时间:2024-05-10 15:47 作者: 来源:
阅读次数:<script>_showDynClicks("wbnews", 1396647413, 1913)</script>
<span id="dynclicks_wbnews_1913_253" name="dynclicks_wbnews_1913_253">2819</span>次
</div>
可知需要先获取id为view_info的div标签。
下面这个函数实现了获取这个标签的功能。
def get_info(fp):
soup = BeautifulSoup(fp, 'lxml')
info = soup.find(id="view_info")
return info
fp是html原文,lxml是解析器,注意这个需要额外安装。
当从网页上获取HTML内容时,通常会将其传递给BeautifulSoup对象来解析。
然后对这个BeautifulSoup对象使用find方法寻找我们想要的标签,指定id为view_info。
下图截取自BeautifulSoup4.12官方文档。
3. 获取两个参数
下面这段代码可以获取两个参数:
def get_visits(info):
script = info.find("script")
match = re.search(r'_showDynClicks\("wbnews", (\d+), (\d+)\)', script.string)
data1, data2 = match.groups()
原理是这样的,首先对上面获取到的<div id="view_info">标签再深入查找,寻找script标签。
如果print一下,可以看到获取到的script就是下面这个:
<script>_showDynClicks("wbnews", 1396647413, 1913)</script>
接下来我们需要把这两个参数提取出来,我们使用正则表达式。
re.search函数用于在整个字符串中搜索第一个与正则表达式匹配的内容。
反斜杠加小括号表达括号的字符,这是转义的意思。
'\('表示'('
'\)'表示')'
而后面的(\d+)则起捕获作用,它代表着由一个或多个数字构成的一个纯数字字符串。
然后通过match.groups()方法就可以将这两个(\d+)分别提取出来,data1就是1396647413,data2就是1913。
4. 获取访问次数
获取到两个参数之后,就可以通过构造requests请求的方式获取访问次数了:
def get_visits(info):
script = info.find("script")
match = re.search(r'_showDynClicks\("wbnews", (\d+), (\d+)\)', script.string)
data1, data2 = match.groups()
resulturl = base_url + 'system/resource/code/news/click/dynclicks.jsp?clickid={}&owner={}&clicktype=wbnews'.format(data2, data1)
response = requests.get(resulturl)
return int(response.text)
这里比较简单,就不多赘述了。
最后取int是因为返回的访问次数是一个字符串,而我希望以整型的方式去做后续处理。
四、全部代码
全部代码如下:
import re
from bs4 import BeautifulSoup
import requests
base_url = 'http://www.dxsjs.sdu.edu.cn/'
def get_html(url):
url = base_url + url
response = requests.get(url)
# 显式指定编码
response.encoding = 'utf-8'
return response.text
def soup(fp):
soup = BeautifulSoup(fp, 'lxml')
info = soup.find(id="view_info")
visits = get_visits(info)
return {
"content": soup.find_all("div", class_="v_news_content")[0].get_text(),
"title": soup.find(id="view_title").get_text(),
"date": info.get_text()[5:15],
"visits": visits,
}
def get_visits(info):
print(info)
script = info.find("script")
print(script)
match = re.search(r'_showDynClicks\("wbnews", (\d+), (\d+)\)', script.string)
data1, data2 = match.groups()
resulturl = base_url + 'system/resource/code/news/click/dynclicks.jsp?clickid={}&owner={}&clicktype=wbnews'.format(data2, data1)
response = requests.get(resulturl)
return int(response.text)
if __name__ == "__main__":
res = soup(get_html("info/1003/1913.htm"))
print(res)