前言
本文笔者吸取了第一次爬取微软翻译的经验,发现在对微软翻译进行请求的时候,URL会随着会话结束而重新生成,这一点是本文需要解决的内容,且除了URL中出现的IG
和IID
两个数据外,在请求的表单数据中,还有key
和token
两个数据也在动态变化。本文旨在追踪这几个数据在何时被发送,如何被获取。
分析
首先,重新打开浏览器自带的开发者工具,随便翻译一词,让客户端发送一次翻译请求ttranslatev3
,可以发现,这次URL与前一篇文章PyQt5实战——翻译的实现,第一次爬取微软翻译经验总结(八)的又不一致
笔者借助开发者工具自带的搜索框进行搜索,看看我们需要的数据在哪里出现过
除了上一篇文章中提到的IG
和IID
两个数据外,我们还需要找到token
和key
两个数据,在ttranslatev3
响应请求中,可以看到我们发送请求时,请求表单除了携带需要翻译的文本,翻译前的语种,翻译后的语种外,还需携带两个特殊的信息
至于tryFetchingGenderDebiasedTranslations
一直为true
,因此直接填写即可
IG
找到IG
所在
首先看看IG
在哪里出现过,将IG
的数据DFFF2F46AAE0482491F67EEEB26C249C
复制进搜索框查询,得到以下信息
可以发现,搜索出来的大部分请求中,这一串数据都是出现在URL与path中的,也就是说,这些请求中,IG
数据也是被使用的,并不是出自于这些请求,继续向下滑动,找到了一个translator
的包
如果你点击进去,就会发现,这其实就是访问该页面的第一个请求响应,它的html数据构建了整个页面
现在我们查看IG
数据在这个响应的哪里,点击刚刚搜索的地方,会自动跳转到response
出现了相同信息的地方,如下图所示:
因此,现在重新确认一下目标:目标从“找到IG
数据的出处”变更为“获取IG
数据”。
请求获取IG
数据
来观察一下这个请求响应,回顾一下上一篇文章,我们在请求时需要些什么数据:
URL
、User-Agent
、表单数据
那我们依次来找这个请求所需要的数据,首先看标头:
URL
这个URL携带的信息很好猜,https://cn.bing.com/translator
是访问的主机,即微软搜索引擎bing的翻译页面,?
表示后面携带了多个参数,ref=TThis
这通常表示一个参考信息或来源标识符。可能是用来跟踪用户从哪个链接访问了翻译工具,text
表示默认的翻译文本,from
表示翻译前的语种,to
表示目标语种。可以看到,这里后面携带的三个信息,正好是打开网页时的默认信息
“输入文本”是背景,实际上并没有东西在这里。
可以猜测,每次访问这个页面,URL
大概率是不会发生变化的,没有携带什么特殊的信息。经过反复的刷新和测试,验证了这个猜想。
User-Agent
为什么User-Agent
会是一样的?需不需要担心它会不会变化?来看一下User-Agent
的定义:
可以看出,一些服务器会根据User-Agent
来判断访问请求是不是来自人类或自动化脚本,可做一些反爬虫操作。我们从浏览器获取的请求信息中包含的User-Agent
,表明了这段请求时来自浏览器,而非自动化脚本,因此,我们可以回答上面的问题:1.User-Agent
一样是因为我们今天与昨天用的都是同一个浏览器访问,它代表了“该请求来自Edge浏览器”。2.无需担心它会发生变化,除非浏览器更新会更改这项数据,而这大概率是不会发生的。
代码实现
首先,先获取整个response的html数据,把它写在一个单独的文件里,(数据量太大,操作台无法查找相应的数据),有些导入的模块会在后面的代码中用到,这仅是完整代码的一部分
import requests
import re
from bs4 import BeautifulSoup
from urllib import response
import urllib.request
import json
url = "https://cn.bing.com/translator?ref=TThis&text=&from=zh-Hans&to=en"
header = {"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0"}
response = requests.get(url,headers=header,data={})
response.raise_for_status()
html = response.text
with open("translator.html", "w", encoding="utf-8") as f:
f.write(html)
获取到相应的HTML,直接搜索IG
现在,我们找到了这个数据,用正则表达式将数据获取,其中re.search
方法是调用第三方库re
ig = re.search(r'IG:"(\w+)"',html).group(1) # 使用正则表达式在html文件中查找IG数据并获取
IID
我们用同样地办法查找IID
的所在地并获取它
找到IID
的所在
找到IID
数据的所在,依然在translator
请求响应中,这就好办了,因为该请求的完整相应已经被我们获取了,只要找到相应的位置即可。
刚刚获取的HTML文件中,你可以找到多个带有data-iid
属性的<div>
,因此,我们需要更加详细的信息来确认其位置,比如利用前面的id=" tta_outGDCont"
,
代码实现
可以使用第三方库BeautifulSoup
,获取html格式数据中特定的属性,比如data-iid
soup = BeautifulSoup(html, "html.parser")
dev_element = soup.find("div", id = "tta_outGDCont")
data_iid = dev_element.attrs["data-iid"]
print("data_iid:"+data_iid)
我们来解释一下soup = BeautifulSoup(html, "html.parser")
这一段代码
剩下的代码看方法名称也很好理解它是做什么的
token与key
找到token
与key
的所在
我们用相同的办法,通过搜索框查找token的所在地,发现,这个信息同样存在于translator
请求响应中:
如果你仔细一点,你会发现,token前面那串数字,就是我们下一个要找的key!真是得来全不费工夫,剩下的两个数据被我们一次性找到了,接下来就是获取它们。
代码实现
pattern = r'var params_AbusePreventionHelper = \[(\d+),"([^"]+)",\d+\];'
token = re.findall(pattern, html)
print("key:"+token[0][0])
print("token:"+token[0][1])
同样地,我们使用正则表达式,查找var params_AbusePreventionHelper
后面的两个数据,放在token列表中。
小结
至此,我们找到了我们所需的全部数据,接下来只需要将IG
与IID
填进URL
中,将token
与key
填进表单中,就可发送完整的请求,伪装成正常的浏览器请求了。
请求与获取相应
代码实现
url = "https://cn.bing.com/ttranslatev3?isVertical=1&&IG="+ig+"&IID="+data_iid
print(url)
header = {"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0"}
data = {"fromLang":"zh-Hans","to":"en","token":token[0][1],"key":token[0][0],"text":"今天的天气","tryFetchingGenderDebiasedTranslations":"true"}
print(data)
data = urllib.parse.urlencode(data).encode("utf-8")
req = urllib.request.Request(url, data, headers=header)
response = urllib.request.urlopen(req)
html = response.read().decode("utf-8")
target = json.loads(html)
print(target[0]['translations'][0]['text'])
根据上一篇文章的经验,以及本文上述的分析:
- URL需要根据上文所获取的IG与IID进行动态变换
data
数据,将我们上面获取的token与key分别放入字典中- 对数据进行UTF-8编码转换格式
- 发送请求
- 获取响应并进行UTF-8解码
- 转换JSON格式
- 获取数据
如果将进行UTF-8解码后的数据完整打印出来,将会是:
[
{
"translations":[
{
"text":"Today's weather",
"to":"en",
"sentLen":{
"srcSentLen":[
5
],
"transSentLen":[
15
]
}
}
],
"detectedLanguage":{
"language":"zh-Hans"
}
},
{
"inputTransliteration":"jīntiān de tiānqì"
}
]
我们要的就是translations
列表下的第一个字典中text
对应的value
完整代码
from csv import Error
from email.policy import HTTP
from tkinter import E
from urllib.error import URLError
import requests
import re
from bs4 import BeautifulSoup
from urllib import response
import urllib.request
import json
class Translation:
def __init__(self,content):
self.translating(content)
def translating(content,lfrom,lto):
if content == "":
return "请输入内容"
langfrom = {"自动检测":"auto-detect","中文":"zh-Hans","English":"en"}
langto = {"中文":"zh-Hans","English":"en"}
url = "https://cn.bing.com/translator?ref=TThis&text=&from=zh-Hans&to=en"
header = {"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0"}
try:
response = requests.get(url,headers=header,data={},timeout=5)
except Exception as e:
print(f"There are something wrong with the network: {e}")
return "website is not reachable"
html = response.text
soup = BeautifulSoup(html, "html.parser")
dev_element = soup.find("div", id = "tta_outGDCont")
data_iid = dev_element.attrs["data-iid"]
print("data_iid:"+data_iid)
ig = re.search(r'IG:"(\w+)"',html).group(1)
print("IG:"+ig)
pattern = r'var params_AbusePreventionHelper = \[(\d+),"([^"]+)",\d+\];'
token = re.findall(pattern, html)
print("key:"+token[0][0])
print("token:"+token[0][1])
url = "https://cn.bing.com/ttranslatev3?isVertical=1&&IG="+ig+"&IID="+data_iid
print(url)
header = {"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0"}
data = {"fromLang":langfrom[lfrom],"to":langto[lto],"token":token[0][1],"key":token[0][0],"text":content,"tryFetchingGenderDebiasedTranslations":"true"}
print(data)
data = urllib.parse.urlencode(data).encode("utf-8")
try:
req = urllib.request.Request(url, data, headers=header)
response = urllib.request.urlopen(req)
html = response.read().decode("utf-8")
target = json.loads(html)
except Exception as e:
print(f'There are something wrong with the network: {e}')
return "website is not reachable"
try:
print("translations:"+target[0]['translations'][0]['text'])
except KeyError:
print(target)
return "something was wrong"
return target[0]['translations'][0]['text']
结语
这次,我们完成了对微软翻译的完整爬虫,且可以完美多次长时间地运行,这个脚本功能已经完善,只是还没有嵌入到GUI程序中,下一篇文章,我们将会把这个脚本整理一下,放到脚本工具包中供翻译器调用,且会加装一些网络状况的判断。
如果你看到这里,说明你又变强了!希望你变得更强,感谢你的观看,共同进步!