我们在使用scrapy爬取web页面时,有时会碰到无法获取某些动态页面中的元素的情况。
这时候一般有3种方法:
- 抓包,分析页面中动态元素的真实请求,重新发送请求以获取元素数据
- 使用headless浏览器模拟访问页面,比如可以使用selenium框架实现模拟访问
- 通过代理服务获取完整加载后的web页面
Splash是一个javascript渲染服务,提供了代理web访问的功能。
Splash是一个轻量级web浏览器,提供了基于http的访问api。它使用Python3实现,底层基于Twisted和QT5。
Splash具有以下特性:
- 可以并行处理多个web页面
- 可获取页面的HTML数据,也可以进行页面截图
- 可使用Adblock Plus的过滤规则过滤页面上的图片,以加快页面渲染
- 可在页面上下文环境中执行javascript脚本
- 支持编写lua浏览器脚本
- 能够获取HAR格式的详细的渲染信息
我们看一下如何在centos上安装和运行Splash。
首先安装docker,docker版本不低于17.
curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
启动docker守护进程
systemctl start docker
获取splash image文件
docker pull scrapinghub/splash
启动splash
docker run -it -p 8050:8050 --rm scrapinghub/splash
成功之后,可以访问http://localhost:8050查看splash到底是个什么东东。

Splash提供了若干http api:
- render.html
- 返回web页面的HTML数据
-
curl'http://localhost:8050/render.html?url=http://baidu.com/&timeout=10&wait=0.5'
- render.png
- 返回web页面的png截图
- curl ‘http://localhost:8050/render.png?url=http://baidu.com/’
- render.har
- render.json
在Scrapy中,可以使用scrapy-splash来使用splash的渲染服务,以获取动态页面上的元素。
通过抓包可以看到谷歌的翻译接口需要生成一个token,此功能需要通过js实现。
我们可以使用PyExecJs这个库来实现在python中执行js脚本的功能。
然后直接通过requests调用谷歌翻译接口就行了。
代码如下:
import execjs
import requests
class Py4Js():
def __init__(self):
self.ctx = execjs.compile("""
function TL(a) {
var k = "";
var b = 406644;
var b1 = 3293161072;
var jd = ".";
var $b = "+-a^+6";
var Zb = "+-3^+b+-f";
for (var e = [], f = 0, g = 0; g < a.length; g++) {
var m = a.charCodeAt(g);
128 > m ? e[f++] = m : (2048 > m ? e[f++] = m >> 6 | 192 : (55296 == (m & 64512) && g + 1 < a.length && 56320 == (a.charCodeAt(g + 1) & 64512) ? (m = 65536 + ((m & 1023) << 10) + (a.charCodeAt(++g) & 1023),
e[f++] = m >> 18 | 240,
e[f++] = m >> 12 & 63 | 128) : e[f++] = m >> 12 | 224,
e[f++] = m >> 6 & 63 | 128),
e[f++] = m & 63 | 128)
}
a = b;
for (f = 0; f < e.length; f++) a += e[f],
a = RL(a, $b);
a = RL(a, Zb);
a ^= b1 || 0;
0 > a && (a = (a & 2147483647) + 2147483648);
a %= 1E6;
return a.toString() + jd + (a ^ b)
};
function RL(a, b) {
var t = "a";
var Yb = "+";
for (var c = 0; c < b.length - 2; c += 3) {
var d = b.charAt(c + 2),
d = d >= t ? d.charCodeAt(0) - 87 : Number(d),
d = b.charAt(c + 1) == Yb ? a >>> d: a << d;
a = b.charAt(c) == Yb ? a + d & 4294967295 : a ^ d
}
return a
}
""")
def getTk(self,text):
return self.ctx.call("TL",text)
def google_translate(content):
'''实现谷歌的翻译'''
js = Py4Js()
tk = js.getTk(content)
if len(content) > 4891:
print("翻译的长度超过限制!!!")
return
param = {'tk': tk, 'q': content}
result = requests.get("""http://translate.google.cn/translate_a/single?client=t&sl=en
&tl=zh-CN&hl=zh-CN&dt=at&dt=bd&dt=ex&dt=ld&dt=md&dt=qca&dt=rw&dt=rm&dt=ss
&dt=t&ie=UTF-8&oe=UTF-8&clearbtn=1&otf=1&pc=1&srcrom=0&ssel=0&tsel=0&kc=2""", params=param)
#返回的结果为Json,解析为一个嵌套列表
trans = result.json()[0]
ret = ''
for i in range(len(trans)):
line = trans[i][0]
if line != None:
ret += trans[i][0]
return ret
ret = google_translate('this is a test of translating from english to chinese')
print(ret)
本文参考git上的TranslateTool这个项目。
调试是解决问题修复bug最有效的手段,用惯了gdb来调试c/c++代码,那么pdb就是调试python代码的得力助手。
- 开始调试
python -m pdb test.py
- 查看、设置断点
b:查看当前已设置的所有断点
b [file:]lineNum:在file的lineNum行设置断点
- 运行、单步运行、进入函数、运行到下一断点
run:开始运行程序,进入到脚本的首行
n:单步运行,不进入当前函数
s:进入当前函数,然后使用n命令单步调试该函数
c:继续运行到下一断点出
- 查看变量值
p:查看变量值
- 查看当前代码上下文
l:列出当前代码所在行
- 其他
在CentOS或Ubuntu上安装python3.7的步骤是类似的,区别在于依赖库的安装。具体步骤如下:
1,安装依赖库
- CentOS
yum -y install zlib zlib-devel
yum -y install bzip2 bzip2-devel
yum -y install ncurses ncurses-devel
yum -y install readline readline-devel
yum -y install openssl openssl-devel openssl-static
yum -y install xz lzma xz-devel
yum -y install sqlite sqlite-devel
yum -y install gdbm gdbm-devel
yum -y install tk tk-devel
yum -y install libffi-devel
- Ubuntu
sudo apt-get install zlib1g-dev libbz2-dev libssl-dev libncurses5-dev libsqlite3-dev libreadline-dev tk-dev libgdbm-dev libdb-dev libpcap-dev xz-utils libexpat1-dev liblzma-dev libffi-dev libc6-dev
2,下载python3.7源码,并编译安装
wget https://www.python.org/ftp/python/3.7.0/Python-3.7.0.tgz && tar xzvf Python-3.7.0.tgz && cd Python-3.7.0 && ./configure –enable-optimizations && make && make install
3,创建软链接
[ -f /usr/local/bin/python3 ] && ln -sf /usr/local/bin/python3 /usr/bin/python3 && ln -sf /usr/bin/python3 /usr/bin/python
面向对象三大特征:封装、继承和多态。Python对这三点完美支持。
#封装
class CA(object):
def __init__(self):
pass
def hi(self):
print("CA")
#继承
class CB(CA):
def __init__(self, msg):
self.__msg = msg
def hi(self):
print("CB: %s" % self.__msg)
a = CA()
a.hi()
print(type(a))
b = CB("God")
b.hi()
print(type(b))
#多态
a = b
a.hi()
print(type(a))
模块是python代码重用和隔离的重要组成部分,也是python之所以开发效率高的原因,开源社区提供了大量的模块供我们使用。
我们可以通过import来导入模块,那么对于不同目录下的模块,如何进行导入操作呢?
import是以当前执行的python脚本所在目录为初始目录,来搜索相关模块文件的。首先搜索sys.path目录,然后搜索初始目录。
- python自带模块
- 很简单,直接import就行了,比如:import os, datetime, json
- 第三方模块
- 大多数第三方模块可以通过pip来安装,安装之后直接import
- 自定义模块
- 调用同一目录下的模块:
- 调用同级目录下的模块
- /a.py 调用/a/lib/b.py:from lib import b
- 调用父目录下的模块
- /test/a.py调用/lib/b.py:
import sys
sys.path.append(“..”)
from lib import b