python爬虫程序之百度翻译,pyexecjs模块的用法(python里的js解析库)

目录

百度翻译爬虫程序

1.需求分析

2.URL分析

3.难点:请求参数分析

4.如何生成sign值

5.pyexecjs模块

6.程序设计

7.程序改进思路


 

pyexecjs模块是python爬虫库里关于javaScript的一套程序,它能帮你解析python代码的js代码。 有经验的爬虫程序员应该知道,在你的请求头中有一部分是被js代码加密的,而这一套js加密程序就保存在你当前访问的网站中(事实上就是存在本地),每一次访问都需要调用js做加密再请求。这个机制可以抵挡大部分的爬虫程序,除非你模仿js加密程序之后再做请求。你可以模拟js程序写一段python程序,也可以直接把网页里的js代码复制下来,使用pyexecjs模块来运用。

 

百度翻译爬虫程序

1.需求分析

百度翻译URL:https://fanyi.baidu.com/

我们的目的是写一个python爬虫程序,当输入内容时能输出翻译结果。本程序利用爬虫的功能,借用百度翻译来实现翻译效果,实际上不需要写翻译的代码(你也写不出),但是我们可以利用爬虫向百度翻译发送请求,再将翻译结果输出。从使用者的角度看,与直接使用翻译软件无差异。如果你的项目中需要用到翻译功能,可以写好这样的程序并打包封装,从而提供翻译功能。

 

2.URL分析

第一步是进行URL分析,我们首先要知道向什么URL发请求,显然不是https://fanyi.baidu.com/,这只是百度翻译的首页URL,并不是将翻译结果响应给我们的URL,所以进入控制台Network进行抓包(Ctrl+R刷新页面)。点击XHR,XHR是Ajax异步请求,是很常见的异步交互,百度翻译也是这么做的。我们在XHR下找到一个v2开头的文件,里面有响应的翻译结果。

所以响应我们请求的URL为:https://fanyi.baidu.com/v2transapi?from=zh&to=en

 

3.难点:请求参数分析

看到v2transapi?from=zh&to=en(可能你的和我不一样但是应该都是v2开头的),已知有8个参数。我们要找出哪些是变化的,哪些是不变的。

首先from和to,这个很容易理解,from来自,to转为,这里的意思是zh转为en,也就是中文转英文。这里显然是变化的,考虑到中文转英文,中文转日文,中文转韩文,等等。但是这里为了使程序更简单,我们只做中文转英文,所以这里我们将from和to看成不变的。

再来是query,待翻译内容,肯定是变化的。

接下来我也不知道是否变化,我们在多进行几次翻译,看看是否发生变化。

我们看到,除了sign值发生变化,其他的都没有变化。

 

4.如何生成sign值

不解决这个问题,我们的程序就写不下去,如果sign值是个变化的,在每次请求时,如果你的sign值与后台核对的值不一致,将不会得到结果。那么接下来看看如果获取sign值: 

我们进入控制台,输入ctrl+shift+f(查询),发现sign值存在于两个js文件中

如何判断sign在哪个文件非常重要,还需要你仔细分析,我分析后发现是第一个文件,点击第一个文件,点击我图片中标记处的{},作用是格式化js代码。

按ctrl+f,弹出查询框,输出sign:后看到sign在哪。看到sign后发现他是由一个y方法生成的,那我们再去找y方法。

把鼠标停留在y上弹出一个链接(这就是y方法的位置) 

 看到y方法,他现在又变为e方法了,看来百度在写js时的时候用到了一些方式来处理。看一下js代码,如果你足够专业,你可以去分析它是如何生成sign值的,这里我不建议你去分析,比较耗费时间,而且不一定能出结果。那么我们就复制这一段方法.

我们找到定义这一段代码的头,define,复制define下的所以方法,复制后,你将得到三个方法,a(),n(),e()方法

复制后在你的PyCharm (程序开发软件) 下创建一个.js结尾的文件,将这段代码拷贝过去。

 

5.pyexecjs模块

pyexecjs模块的作用是在python环境下运行js代码。首先安装,打开终端cmd,输入:python -m install pyexecjs

pyexecjs模块有很多方法,在这里我们只需要用到两个

exec_obj = execjs.compile(js文件)     #生成js对象
sign = exec_obj.eval('方法'))              #调用对象的eval方法,eval中再写要调用的js代码里的方法,需要打引号

下面来测试一下刚才那段js代码,我们用hello来测试是否会生成sign

import execjs

with open('translate.js','r') as f:
     data = f.read()
exec_obj = execjs.compile(data)
# 使用js解析出sign
sign = exec_obj.eval('e("hello")'.format(word,gtk))

报错了,window没有定义。我们去看看window在js代码里的何处。

找到window[l](注意这里是字母l不是数字1),这只能说明window[l]这个值并非是本地文件生成的,它是在你发请求(请求翻译)时定义的一个值,所以还需要去控制台分析。

我们首先看看字母l是什么,在看看window[l]会输出什么(在控制台的Source下执行):

原来字母l是gtk,window['gtk']的值就是我们要的。这个值我在百度翻译首页找到了(URL:https://fanyi.baidu.com),查看源代码。所以我们需要先在这个URL下获取到gtk,把获取到的gtk插入到js代码。

 到这里我们的思路是,在百度翻译首页获取到gtk,再修改一下js代码,让e方法多一个参数,把window[l]改为gtk,如下:

 

 

6.程序设计

程序思路是:

1)向URL:https://fanyi.baidu.com/发送请求,获取gtk(生成sign必要的值)

2)向URL:https://fanyi.baidu.com/v2transapi?from=zh&to=en发送请求,包装请求内容data

根据这两个步骤设计两个方法:

1)get_gtk()

def get_gtk_token(self):
    rep=requests.get(url=self.get_url,headers=self.headers).text
    # 采用正则匹配
    gtk=re.findall("window.gtk = '(.*?)'",rep,re.S)[0]

2 ) get_translate()

def translate(self,word):
    # 先拿到gtk和token
    gtk,token=self.get_gtk_token()
    # js解析
    with open('translate.js','r') as f:
        data = f.read()
    exec_obj = execjs.compile(data)
    # 使用js解析出sign
    sign = exec_obj.eval('e("{}","{}")'.format(word,gtk))
    # 封装data
    # 替换query,sign,token的值
    data={
        'from': 'zh',
        'to': 'en',
        'query': word,
        'transtype': 'enter',
        'simple_means_flag': '3',
        'sign': sign,
        'token': token,
        'domain': 'common',
    }
    # 请求并拿到结果
    rep=requests.post(url=self.post_url,headers=self.headers,data=data).json()
    # json格式,拿到翻译内容
    result=rep['trans_result']['data'][0]['dst']
    return result

全部代码如下:

import requests
import execjs
import re

class BaiduTran_spider(object):
    def __init__(self):
        self.get_url="https://fanyi.baidu.com/"
        self.post_url="https://fanyi.baidu.com/v2transapi?from=zh&to=en"
        self.headers={
            'accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
            'accept-encoding':'gzip, deflate, br',
            'accept-language':'zh-CN,zh;q=0.9',
            'cookie':'BIDUPSID=B37B0F707132D26581E31BD5CA6BFE3C; PSTM=1578800714; BAIDUID=1673E9683D9552E98C905372C7B2BFEE:FG=1; BDUSS=GVYWk5pdmpXWkhHZE5NWXZRbDgxZml1dDRHSm11M3BLQ3BjU3JiWGhrd2E2VmhlRVFBQUFBJCQAAAAAAAAAAAEAAABVkN8~ampobGx2emYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABpcMV4aXDFeb; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; REALTIME_TRANS_SWITCH=1; FANYI_WORD_SWITCH=1; HISTORY_SWITCH=1; SOUND_SPD_SWITCH=1; SOUND_PREFER_SWITCH=1; APPGUIDE_8_2_2=1; delPer=0; PSINO=6; BDRCVFR[feWj1Vr5u3D]=I67x6TjHwwYf0; BCLID=11847510826324916119; BDSFRCVID=5r8OJeC629IatZouEnZohb9yEeKt6D6TH6aotc2Up_NExpGb8DoOEG0Pef8g0Ku-8Uu8ogKK3gOTH4PF_2uxOjjg8UtVJeC6EG0Ptf8g0M5; H_BDCLCKID_SF=fRPD_Dt5f-5_jJ7kqtbSMttfqx6betJyaR3uKKnvWJ5TMC_wytQMW440QJD8KtvafGbDoqv2Jq--ShPC-tnM-TDAKbnw0Rvj3KuOKn7v3l02Vb5Ie-t2ynQDebotqPRMW20e0h7mWIbmsxA45J7cM4IseboJLfT-0bc4KKJxbnLWeIJEjjC5jTo0ja0OqbQX2COXsROs2ROOKRcgq4bohjPXb-reBtQm05bxohnF-h7hsJrLW5bKKtABD-b7tfoIynrQBl8bWDFBMKDCD5t35tCthUIL5-70KK0XBJ-8Kb7VbI54eMnkbfJBDGrA5R3za5T4WfcO2n_BJq5Nyn8-Q5K7yajK2b32tHTjMhrNMPJnHCnehUnpQT8rbfDOK5Oib4DO-4Kyab3vOIOTXpO1jh8zBN5thURB2DkO-4bCWJ5TMl5jDh3Mb6ksD-FtqtJHKbDJoCLbfxK; ZD_ENTRY=baidu; H_PS_PSSID=1432_21126_30823; Hm_lvt_64ecd82404c51e03dc91cb9e8c025574=1582296526,1582296537,1582342014,1582342026; Hm_lpvt_64ecd82404c51e03dc91cb9e8c025574=1582342026; __yjsv5_shitong=1.0_7_7696d052662152bccaf65039127a9f65e892_300_1582342026337_113.110.179.193_f52537f7; yjs_js_security_passport=dded934eb182a45eb86797cadd1bbc032e53bf43_1582342043_js; to_lang_often=%5B%7B%22value%22%3A%22zh%22%2C%22text%22%3A%22%u4E2D%u6587%22%7D%2C%7B%22value%22%3A%22en%22%2C%22text%22%3A%22%u82F1%u8BED%22%7D%5D; from_lang_often=%5B%7B%22value%22%3A%22en%22%2C%22text%22%3A%22%u82F1%u8BED%22%7D%2C%7B%22value%22%3A%22zh%22%2C%22text%22%3A%22%u4E2D%u6587%22%7D%5D',
            'sec-fetch-mode':'navigate',
            'sec-fetch-site':'none',
            'sec-fetch-user':'?1',
            'upgrade-insecure-requests':'1',
            'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36',
        }

    # 提取gtk和token的值
    def get_gtk_token(self):
        rep=requests.get(url=self.get_url,headers=self.headers).text
        # 采用正则匹配
        # 获取gtk
        gtk=re.findall("window.gtk = '(.*?)'",rep,re.S)[0]
        # 虽然token是个定值,但是不保证在下次请求时不变,所以还是以网页源码的token为主
        token=re.findall("token: '(.*?)'",rep,re.S)[0]

        return gtk,token

    # js解析,获取sign并做翻译
    def translate(self,word):
        # 先拿到gtk和token
        gtk,token=self.get_gtk_token()
        # js解析
        with open('translate.js','r') as f:
            data = f.read()
        exec_obj = execjs.compile(data)
        # 使用js解析出sign
        sign = exec_obj.eval('e("{}","{}")'.format(word,gtk))
        # 封装data
        # 替换query,sign,token的值
        data={
            'from': 'zh',
            'to': 'en',
            'query': word,
            'transtype': 'enter',
            'simple_means_flag': '3',
            'sign': sign,
            'token': token,
            'domain': 'common',
        }
        # 请求并拿到结果
        rep=requests.post(url=self.post_url,headers=self.headers,data=data).json()
        # json格式,拿到翻译内容
        result=rep['trans_result']['data'][0]['dst']
        return result

    def run(self):
        word=input('请输入要翻译的内容:')
        result=self.translate(word)
        print("翻译结果为:",result)

if __name__=='__main__':
    spdier=BaiduTran_spider()
    spdier.run()

效果:

 

7.程序改进思路

上面的代码只接受中文翻译英文,这是因为这两处,from和to指定了翻译的方向。如果你想改进,你可以试着写一个判断,自动判别操作者输入的是什么语言,这个难度很大,如果是要翻译的内容全是某一种语言还好,如果他们有中有英,那么你如何判断操作者是要向哪个方向翻译?

你可以让操作者自己选择要翻译的方向,这样子会简单很多。

 

  • 12
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值