之所以想做这个 API,主要是我博客上有百度收录检测,但是速度有点慢,后来想想可能是因为服务器在国外,所以就有了把 API 搬到国内的想法。
但是国内的 API 已经用 python 搭建了一个随机图服务,不好再加一个 PHP 吧?算了,干脆我重写一个 API 得了,于是就有了这个项目。代码已经放到 GitHub 上了,欢迎大家交流讨论。
查询次数过于频繁有大概率会查询失败,目前只是半成品,只适合自用
源码里有其他 API,大家直接看 `get_baidu` 即可
Github 地址
Demo:https://api.yanshu.work/baidu/?u=http://www.dkewl.com
思路
原作者检测的方法貌似是直接查询网址,然后匹配有多少搜索结果几条,如果搜索结果大于 0 就认为已收录。但是这种方法有个问题,百度有时候会收录一些与目标网址无关的页面,这样检测会不准确。
网上搜了搜,其他一些文章是查询百度跳转链接,然后一个个获取真实链接,再与查询的网址进行匹配,如果能匹配上就说明已收录。嗯想法很好,理论上准确性非常高,就是有点耗时间……不管怎么样总归是个方案,先试试。
很遗憾,技术有限,我并不能获取百度跳转后的真实链接,Stack Overflow 上给出的解决方法也统统不起作用,这个方案宣告失败。
PS:记录下获取跳转后真实链接的几种方法,说不定以后会用上。
# 方法一r = requests.get('http://techtv.mit.edu/videos/1585-music-session-02/download.source') print(r.url) # http://d1baxxa0joomi3.cloudfront.net/2515a9db659b0ab26d869b4ff2dadca9/original.mov# 方法二:这种适用于多次跳转response = requests.get(someurl)if response.history: print("Request was redirected") for resp in response.history: print(resp.status_code, resp.url) print("Final destination:") print(response.status_code, response.url)else: print("Request was not redirected")# 方法三r = requests.get('http://github.com/', allow_redirects=False) r.status_code # 302r.url # http://github.com, not https.r.headers['Location'] # https://github.com/ -- the redirect destination# 方法四import urllib.request res = urllib.request.urlopen(starturl) finalurl = res.geturl() print(finalurl)
参考资料:https://stackoverflow.com/questions/20475552/python-requests-library-redirect-new-url
分析
只能一步步分析了,首先看个已收录的文章:
再看看没有收录的文章:
突然有了想法,我直接解析 <b>url<b>
不就行了?如果能匹配到就说明已收录,匹配不到就没有收录。顺着这个思路,很快写好了第一版代码。
def get_baidu(url): headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36' } baidu = 'https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&tn=baidu&wd=' search_url = baidu + url response = requests.get(search_url,headers=headers) text = response.text if 'http' in url: url = url[url.index('//')+2:] pattern=re.compile(r'<b>{}</b>'.format(url)) result_list = pattern.findall(text) if result_list: return 200 else: return 403
随便测试了几篇文章,确实有效,可以收工了……且慢,再找找其他人的文章测试一下吧。
输入 http://www.ruanyifeng.com/blog/2017/12/blockchain-tutorial.html ,嗯?怎么返回 403 了?用百度搜了一下,没错啊,已经收录了,而且搜索出来的结果也是加粗的。
仔细看了一下,哦……原来百度把太长的网址隐藏了一部分啊,所以匹配不到。这就难办了,诚然我可以截取网址的一部分进行匹配,但是这样就又不精确了。
我又仔细观察了一下,发现如果百度确实收录了,那么这个网址会在搜索结果的最上方,而且第一条和第二条搜索结果之间会有“以下是网页中包含……”的提示信息。于是我有了个新的想法,首先截取网页文本从开头到“以下是网页中……”的内容,然后用正则匹配百度跳转链接,如果能匹配得到说明百度第一条有搜索到的网页结果,那这篇文章肯定收录了。于是我写出了第二版代码。
@app.get("/baidu/")async def get_baidu(u: str = Query(..., min_length=3)): headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36', 'Referer': 'https://www.baidu.com' } # 网址后缀处理 if '.' in u[-5:]: pass elif u[-1] != '/': u += '/' # 网址协议头处理 if ('http' in u) and not ('www' in u): u = 'www.' + u[u.index('//')+2:] elif ('http' not in u) and not ('www' in u): u = 'www.' + u baidu = 'https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&tn=baidu&wd=' search_url = baidu + u try: response = requests.get(search_url, headers=headers) text = response.text text = text[:text.index('以下是网页中包含')] pattern = re.compile(r'"http://www.baidu.com/link\?url=.+?"') result_list = pattern.findall(text) if result_list: return {'code': 200, 'msg': '该网址已被百度收录!'} else: return {'code': 403, 'msg': '该网址暂未被百度收录!'} except: return {'code': 404, 'msg': '百度收录查询失败!'}
嗯,很好,这次是真的可以收工了……(Flag 预定)。
PS:可以看到代码里有个网址的预处理,这是因为网址没有最后一个 /
时会查不出来,但是 .html
这种格式的又不需要加。另外,网址开头可以不带 http/https
,但是必须带 www
,要不然同样搜不出来。
跨域问题
测试没问题,那就可以上线了,部署到服务器上,按照 https://www.sitstars.com/archives/65/中的教程修改 API 地址,看下情况。问题又来了……
API 请求地址出现了一个鲜红的红色,提示 strict-origin-when-cross-origin
错误,看样子是跨域错误?试着用错误提示 +fastapi
在 Google 上搜了一下,果然 fastapi 早已经考虑到了这个问题,并且给出了解决方案。
参考资料:https://fastapi.tiangolo.com/tutorial/cors/
简单来说,加入以下代码就可以了:
from fastapi import FastAPIfrom fastapi.middleware.cors import CORSMiddleware app = FastAPI() origins = [ "http://localhost.tiangolo.com", "https://localhost.tiangolo.com", "http://localhost", "http://localhost:8080", "*"] app.add_middleware( CORSMiddleware, allow_origins=origins, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], )
origins
是一个所有允许的请求域列表,加入 *
代表允许所有请求,当然也可以加入具体的网址和端口,比如上面几个例子。
加好后再测试一下,成功显示!而且速度还挺快。
百度自身问题
正当我开心地刷新网站看效果时,悲剧了,网页上突然提示我百度收录查询失败!这是为何?手动试了一下 API,果然输出不了结果,但是本地使用又完全没有问题。莫非是我搜得过于频繁导致 ip 被百度拉黑了?于是我找了很多种解决方案,比如 headers
添加更多参数,更换代理 ip,完全没用!
没办法我又看了原作者的博客,注意到 PHP 版本 API 的代码里有个 CURLOPT_FOLLOWLOCATION
,百度一下发现它的功能是跟随网址的重定向。嗯……会不会是服务器搜索太快导致百度重定向没反应过来?于是我借助第二节的代码,print()出最终跳转后的网址,看看它到底给我定向到哪了。
修改好代码后再次部署,没想到这次 API 又有用了,看来是抽风性质的问题。刷新了几次又不能用了,而此时服务器的控制台上也刷出了重定向后的网址,和我拼接的百度查询网址一样!这又是什么情况?我自身对网络方面知之甚少,所以最终也没有解决方案,只是知道了判断百度查询失败的另一个方法:只要有重定向那就一定失败了。
1.本站大部分内容均收集于网络!若内容若侵犯到您的权益,请发送邮件至:duhaomu@163.com,我们将第一时间处理!
2.资源所需价格并非资源售卖价格,是收集、整理、编辑详情以及本站运营的适当补贴,并且本站不提供任何免费技术支持。
3.所有资源仅限于参考和学习,版权归原作者所有,更多请阅读网站声明。