非凡 发表于 2021-10-6 16:14:57

python爬虫有道翻译:post响应相关问题

本帖最后由 非凡 于 2021-10-6 16:25 编辑

我在C语言中文网读到一篇文章,讲爬虫有道翻译
http://c.biancheng.net/python_spider/case05.html
我截取以下一段内容:

通过控制台抓包,我们得知了 POST 请求的参数以及相应的参数值,如下所示:

并发现以下了规律:salt、sign、lts 总是变化的,而 bv 等其他参数是不变化的。其中 lts 代表毫秒时间戳,salt 和 lts 之间存在着某种关联,因为两者只有最后一个数字是不同的;而 sign 对应的值是一个加密后的字符串。

如果想要实现实时地抓取翻译结果,就需要将 salt 和 sign 转换为 用 Python 代码表示的固定形式。最后将所有参数放入到 requests.post() 中,如下所示:
response = requests.post(url,data=data,headers=headers)
其中 data 是字典格式参数,它用来构建 POST 请求方法的参数和参数值。

接下来一段是如何寻找页面上的JS代码salat与sign,与我需要解决问题无多大关系,就不复制过来了,感兴趣可以访问原链接。

通过上述方法就找到了 salt 与 sign(两个参数项是在一起的)JS 代码,如下所示:
var r = function(e) {
    var t = n.md5(navigator.appVersion),
    r = "" + (new Date).getTime(),
    i = r + parseInt(10 * Math.random(), 10);
    return {
      ts: r,
      bv: t,
      salt: i,
      sign: n.md5("fanyideskweb" + e + i + "Tbh5E8=q6U3EXe+&L[4c@")
    }
};

通过上述 JS 代码的简单分析可知: r 变量等同于 lts,salt 变量等同于 i,而 sign 是一个经过 md5 加密的字符串。接下来使用 Python 代码来表示上述参数,如下所示:

#lts毫秒时间戳
str(int(time.time()*1000))
#salt, lts+从0-9的随机数
lts+str(random.randint(0,9))
#sign加密字符串
from hashlib import md5
#word为要翻译的单词等同于js代码中的"e"
string = "fanyideskweb" + word + salt + "Tbh5E8=q6U3EXe+&L[4c@"
s = md5()
#md5的加密串必须为字节码
s.update(string.encode())
#16进制加密
sign = s.hexdigest()
文章中的完整程序实现代码是:

#coding:utf8
import random
import time
from hashlib import md5
import requests


class YoudaoSpider(object):
    def __init__(self):
      # url一定要写抓包时抓到的POST请求的提交地址,但是还需要去掉 url中的“_o”,
      # “_o”这是一种url反爬策略,做了页面跳转,若直接访问会返回{"errorCode":50}
      self.url='http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule'
      self.headers={
      "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36",
      }
    # 获取lts时间戳,salt加密盐,sign加密签名
    def get_lts_salt_sign(self,word):
      lts=str(int(time.time()*1000))
      salt=lts+str(random.randint(0,9))
      string = "fanyideskweb" + word + salt + "Tbh5E8=q6U3EXe+&L[4c@"
      s=md5()
      s.update(string.encode())
      sign=s.hexdigest()
      print(lts,salt,sign)
      return lts,salt,sign

    def attack_yd(self,word):
      lts,salt,sign=self.get_lts_salt_sign(word)

      #构建form表单数据
      data={
            "i": word,
            "from": "AUTO",
            "to": "AUTO",
            "smartresult": "dict",
            "client": "fanyideskweb",
            "salt": salt,
            "sign": sign,
            "lts": lts,
            "bv": "cda1e53e0c0eb8dd4002cefc117fa588",
            "doctype": "json",
            "version": "2.1",
            "keyfrom": "fanyi.web",
            "action": "FY_BY_REALTlME"
      }
      #使用 reqeusts.post()方法提交请求
      res = requests.post(
            url=self.url,
            data=data,
            headers=self.headers,

      )
      # res.json() 将json格式的字符串转为python数据类型
      # 客户端与服务器数据交互以json字符串传递,因此需要将它转换为python数据类型
      html=res.json()
      print(html)
      # 查看响应结果responsehtml:{"translateResult":[[{"tgt":"hello","src":"你好"}]],"errorCode":0,"type":"zh-CHS2en"}
      result=html["translateResult"]["tgt"]
      print('翻译结果:', result)

    def run(self):
      try:
            word=input('请输入要翻译的单词:')
            self.attack_yd(word)
      except Exception as e:
            print(e)

if __name__ == '__main__':
    spider=YoudaoSpider()
    spider.run()

关于爬虫爬有道翻译,我在小甲鱼的视频里也看过,但是不同的地方是,关于post里的data集,小甲鱼视频里似乎没像上面一样去考虑salt,sign,lts三个参数的变化问题,直接是将网页里的post响应里的原样复制进去就行了,我操作过,代码如下:
import urllib.request
import urllib.parse
import json
content = input('请输入需要翻译的内容:')
url = 'https://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule'

data = {}
data['i'] = content
data['from']= 'AUTO'
data['to']= 'AUTO'
data['smartresult']= 'dict'
data['client']= 'fanyideskweb'
#此处的salt、sign、lts三个参数值没有变化
data['salt']= '16315470681987'
data['sign']= 'd6b63a7371c65430a325ca72e9cef1dd'
data['lts']= '1631547068198'
data['bv']= '5912a9bc00e8093f5992b73a3708e1b5'
data['doctype']= 'json'
data['version']= '2.1'
data['keyfrom']= 'fanyi.web'
data['action']= 'FY_BY_CLICKBUTTION'

data = urllib.parse.urlencode(data).encode('utf-8')   # encode('utf-8')将 Unicode 编码转换成utf-8形式


# 修改headers信息User-Agent参数的方法一:
    # 建立head字典,将User-Agent的值修改未正常浏览器访问网页是用的值()
head = {}
head['User-Agent'] ='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36 Edg/93.0.961.47'
req = urllib.request.Request(url,data,head)             #在Request时传入head参数


# 修改headers信息User-Agent参数的方法二:
req = urllib.request.Request(url,data)
    #通过add_header()追加head参数
req.add_header('User-Agent','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36 Edg/93.0.961.47')

response = urllib.request.urlopen(req)

html = response.read().decode('utf-8')          #以utf-8去解码,转换成 Unicode 编码( Unicode 编码是python的默认编码格式)

print(html)             #此时的html是个字符串,是给json语句的字符串


target = json.loads(html)       #将字符串载入

原文 = target['translateResult']['src']
译文 = target['translateResult']['tgt']
print(原文+':'+译文)


两段代码实现的效果是一样的,这是为什么?
salt、sign、lts三个参数值在网页中确实每次都是变化的,为什么用代码去爬的时候又不需要去考虑它的变化也能爬成功呢?

suchocolate 发表于 2021-10-6 18:26:23

虽然浏览器根据js提交了其他参数,然而参数有没有用,有没有被web服务器用来做校验或授权,这个要看服务器侧的设置。
就目前的现象看,对于有道翻译,我们只需要提交数据中的i即可,其他无从可知。

非凡 发表于 2021-10-7 11:03:23

suchocolate 发表于 2021-10-6 18:26
虽然浏览器根据js提交了其他参数,然而参数有没有用,有没有被web服务器用来做校验或授权,这个要看服务器 ...

感谢回答
页: [1]
查看完整版本: python爬虫有道翻译:post响应相关问题