前言
AI的发展日新月异,及时掌握一些AI的消息和妹子聊天时也不至于词穷(不建议和妹子聊技术)。
所以这里就以36氪和虎嗅网为例,来讲一下如何爬取AI新闻消息以及数据整合。36氪和虎嗅网这两个网站新闻爬虫比较具有代表性,36氪是传统的html网页爬虫,虎嗅网是异步api加载加密的爬虫,这里就从简单的36氪讲起。
36Kr
在36Kr通过搜索框输入AI,然后在快讯频道就可以看到最新的AI咨询。
对于判断爬取一个网站是使用传统html方式,还是异步加载的方式,最简单的方法就是“搜索”。根据从网页上看到的信息关键字进行搜索,来看看关键字出现在哪个请求中。
可以看到关键字出现在了html中,而不是xhr接口中,所以在36Kr获取最新的AI快讯就就是对HTML进行解析即可。接着看看HTML请求头信息,获取URL来获取HTML。
通过python的requests模块发起请求,最后解析目标数据实现36Kr AI快讯爬虫的代码开发。在python中,对html常用的解析方式有:BeautifulSoup和Xpath,因为之前写scrapy爬虫比较多,所以我个人比较喜欢使用scrapy.Selector,这里推荐大家用一下。
import requests
from scrapy import Selector
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36',
}
url = 'https://36kr.com/search/newsflashes/AI'
response = requests.get(url, headers)
response = Selector(response)
articles = response.css('div.newsflash-item')
for article in articles:
title = ''.join(article.css("a ::text").extract())
url = 'https://36kr.com' + article.css("a::attr(href)").extract()[0]
time = article.css("span.time::text").extract()[0]
print(title, time, url)
Selector支持xpath和css语法,我这里使用的是css,这里只讲一个知识点:在获取title的哪行代码,通过a ::text获取a标签以及a标签下所有元素的文本内容。这里一定是有空格的,没有空格的话,只能获取a标签的文本。
最后解析了title、url、time三个字段,如果想爬取具体的新闻内容,可以在在for循环中对获取的url发起请求,在进行解析即可。
虎嗅网
虎嗅网同样也是在搜索框输入“AI”,搜索AI有关的资讯。在控制台通过搜索关键字,我们在xhr异步接口中发现了新闻咨询内容。
如图,新闻数据通过接口请求返回json的方式渲染的,而非36Kr返回的HTML,所以虎嗅网AI新闻咨询爬虫就是一个比较常见的XHR动态加载的爬虫。
查看请求头,分析请求需要的参数:
从platform到pagesize参数这里就不多讲了,appid是固定的。
有兴趣的可以打断点看看,这里主要讲讲nonce、timestamp、signature三个参数。
nonce、timestamp、signature
在 j(t) 方法中,可以看到nonce、timestamp、signature的构造过程。
nonce是调用 D() 方法生成的字符串,就是从[A-Z|0-9|a-z]中随机抽取16个字符。
function D() {
for (var t = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : 16,
e = "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ",
n = "", i = 0; i < t; i++)
n += e.charAt(Math.floor(Math.random() * e.length));
return n
}
为了表现得更直观,打上了断点。
timestamp就是10位的时间戳,signature调用了一个 f 方法,参数是列表r。这个列表r是怎么来的呢?是将一个固定字符串n、timestamp、nonce放进去,然后进行排序,最后将三个元素拼接成一个字符串进行加密。
我们接着探究f():
t就是传进来的三合一的参数。然后就是密密麻麻的各种字节运算、位运算。正常情况下,是先将一行行js代码读懂,然后转换成python实现,最后返回signature。这就有点浪费时间了...
Don't Worry!!经常搞爬虫的朋友都知道,这其实就是个SHA-1加密,在python都是有现成的库...
所以
import requests
import math
import random
import time
import hashlib
import json
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36',
'Referer': 'https://www.huxiu.com/',
'Origin': 'https://www.huxiu.com',
'Content-Type': 'application/json'
}
def D():
e = "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ"
n = ""
for i in range(0, 16):
n += e[math.floor(random.random() * len(e))]
return n
nonce = D()
timestamp = int(time.time())
n = 'hUzaABtNfDE-6UiyaYhfsmjW-8dnoyVc'
lists = [str(timestamp), str(n), str(nonce)]
lists.sort()
t = ''.join(lists)
hash_object = hashlib.sha1(t.encode())
signature = hash_object.hexdigest()
s = 'ai'
page = 1
pagesize = 20
url = f'https://search-api.huxiu.com/api/article?platform=www&s={s}&sort=&page=1&pagesize=20&appid=hx_search_202303&nonce={nonce}×tamp={timestamp}&signature={signature}'
data = {
'platform': 'www',
's': s,
'sort': '',
'page': 1,
'pagesize': 20,
'appid': 'hx_search_202303',
'nonce': nonce,
'timestamp': timestamp,
'signature': signature
}
response = requests.post(url, headers)
data = json.loads(response.text)
if data:
datalist = data['data']['datalist']
for article in datalist:
title = article['title'].replace('</em>', '').replace('<em>', '')
url = article['url']
author = article['author']
print(title, author, url)
signature直接使用hashlib库一行代码搞定,返回的是json格式的数据,使用json模块加载解析即可,最后输出三个字段:title、author、url。
问题排查
在程序测试的过程中,一直遇到“签名数据不正确”的响应数据。
遇到这个问题,我一共做了两步排查。
-
将程序中用户生成signature的参数粘贴到浏览器断点中测试,调用js生成的signature和程序程序中生成的一致,这说明我的程序代码逻辑是没问题的。
-
我从浏览器控制台复制了url,使用http客户端进行测试,最后也能请求到数据,说明接口也是没问题的。
于是,我就迷茫了。后来又打了一次断点,发现n这个变量发生了错误,在代码中,你可以看到我使用的是'hUzaABtNfDE-6UiyaYhfsmjW-8dnoyVc'。
其实这个n的初始值是'w-Ui'结尾的,后面又计算赋值修改了其他值,我程序中刚开始用的就是初始值,所以虽然signature生成逻辑没有错,但是参数错了。
结语
这就是我使用爬虫爬取AI新闻的过程,使用了两个爬虫中比较常见的典型案例。像这种类别信息的采集,还有更优的程序设计架构。
上面的程序没有设计存储模块,如果设计了采集模块,且采集了许多网站的AI新闻消息的话,我建议使用scrapy的架构做一个统一的管理,包括程序、存储模块、并发、参数等等的统一管理,架构图放下面,有兴趣的可以了解一下。