为了解决问题也为了练习,做一些事情!

(看起来简单的事情还是踩到了一些坑,现在为止虚拟环境大致清楚了,最后还剩一个坑))

首先,装一个anaconda。从windows上复制过去,有两种方法:1 ZMODEM,XShell自带

2、走scp……

(速度慢,是卡在服务器那头的带宽……所以没法比较)

转好之后需要改家目录下的环境变量.bashrc,可以安装时按yes,自动帮你改,如果错过了也可以自己去vi。

为了让环境变量生效通用的说法是重开终端,不过我的实践貌似要连xshell整体关了重开才行。

环境变量末尾新增这一行这就是验证方法,对conda有反应说明变量真的加好了

接下来用conda创建虚拟环境。

(顺路说说conda和anaconda还有miniconda的区别,这个答案说得太好了 What are the differences between Conda and Anaconda 简洁明了!说得太好忍不住点了赞~)

conda创建虚拟环境用这个命令

conda create -n env_name [python=3.6]

可以看到,我并没有为当前用户建任何虚拟环境,但source activate看起来依然进入了一个叫base的环境,但是这其中的which python又不是虚拟环境下的路径……好奇怪!

其实原因就是,现在进入的并不是虚拟环境,而是所有虚拟环境的base,还是实体环境提供的base……所以一定要带上后面的env_name(source activate env_name) 或者最保险的是用绝对路径,就不用依赖环境变量

现在来真的建一个,指定python版本3.6。

然后进入环境,在环境中安装包 pip install -r requirements.txt

然后在环境中执行最近报错的脚本示例,我真是担心这个bug能不能修掉!

在stackoverflow和作者的github上都提问了,暂时都没回应:pyppeteer.errors.BrowserError: Failed to connect to browser portstackoverflow.com

主要是用了一个叫requests_html的库,跟requests一样请求网页,其中有个render方法在没办法搞清楚页面加载逻辑的时候可以粗暴地使用。

from requests_html import HTMLSession
s = HTMLSession()
url = "http://python-requests.org"
r = s.get(url)
if r.status_code == 200:
r.html.render() #在这一行报错
print(r.text[:20])

果然,还是报错!不过报错信息不一样!

现在报超时,另一台服务器上报BrowseError。

不过我检查了模块内部。这个BrowseError也是raise的。模块内部,这里可以看一下。开始是在下载chrome浏览器,给render用

保险起见,看到timeout还是检查一下网络通不通。ping的时候记得去掉http://,那是定义本次如何访问域名的协议的名称,后面才是域名,域名才能被换算成IP。

网络是通的,我猜网络也是通的……

报错为什么不一样,看版本号……是一样的啊!0.9.0,最近对这个数字很熟悉

希望能够解决,不然……

直觉还是操作系统环境配置的差异。

明天准备借用同事的环境试试,另外准备在模块出错的附近手动打印(还好不算天书……),查查具体是什么问题。

今天看模块内部的逻辑,这里的url就是系统随机取了一个空闲的端口,绑定给chrome做代理用。然后转个身怎么就connect不了了。自己分配的端口怎么就不认了呢,无语(~再仔细看一下)

【0624更】

1、render()失败包BrowserError的问题已解决,差lib库,但具体需要哪些我也不知道。

2、阿里云上的render()其实第一次就成功了,看到的报错不是BrowserError而是MaxRetries,前者是找不到浏览器,后者是已经发起请求但响应超时。事实上可能是请求的url问题,换成简单的百度就可以render成功打印页面内容。

附测试脚本:

from requests_html import HTMLSession
s = HTMLSession()
url = "http://www.baidu.com"
r = s.get(url)
if r.status_code == 200:
r.html.render()
print(r.text[:20])

不过上周在生产中另外发现一个新问题,render()一次就会打开很多chrome进程且不会自动关闭,同事说这个有些棘手,因为这个模块自身确实就不带关闭的,生产环境最好换另外的库phantjs什么的……emmm……不管怎么样,这次我还是想先解决了再说。

我觉得这个答案里面的会有帮助:Issue Rendering Javascript in a Thread · Issue #155 · kennethreitz/requests-htmlgithub.com
>>> from threading import Thread
>>> from requests_html import HTMLSession
>>> session = HTMLSession()
>>> session.browser
>>> def render_html():
... r = session.get('http://python-requests.org/')
... r.html.render()
...
>>> t = Thread(target=render_html)
>>> t.start()session.browser.

重点是这里的session.browser,看模块的内部代码就可以发现,这个browser是一个被@ property装饰过的属性,本身是一个方法,用于产生一个browser。这个答主的意思应该是,先统一执行这一句,把browser固定下来,而不是每开1个线程就去构造1个。(思路很好)

我先测试一下。

【10:03更】

解决了!

顺序render()了 5000多个页面,从始至终 服务器上 的chrome进程 只有4-5个,这样应该是可以接受了。

还没有用到上述回答里提到的session.browser.

昨天在服务器上之所以会有大量滞留的进程,是因为每次需要render一个url,都重新构造了一个HTMLSession对象,然后对这个对象调用render方法会制造chrome进程但是用完后又不会关闭,除非这个python程序关闭……所以,问题就在这里了。

解决,HTMLSession对象在初始化的时候构造一次,以后多次render的时候调用同一个HTMLSession对象就是了,反正render()在时间上都是顺序执行,不会有其他问题。

class StockData(object):
def __init__(self):
self.codes = []
self.session = HTMLSession()
def spider(self, code):
"""资产负债表"""
url = 'xx'
r = None
try:
r = self.session.get(url)
r.html.render()