-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
5 changed files
with
264 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
### 爬虫定时任务不能正常工作 | ||
|
||
#### 一句话总结:需要依赖变量的cron任务,请务必指定环境变量 | ||
|
||
我的爬虫任务是通过crontab来进行调度的。爬虫是通过运行在虚拟环境中的,通过sh命令启动。代码如下: | ||
|
||
|
||
``` | ||
# 文件名task.sh | ||
source data_venv/bin/activate | ||
python spider.py | ||
python parser.py | ||
deactivate | ||
``` | ||
|
||
cron任务是这样写的: | ||
``` | ||
0 */2 * * * cd /data/Blog_Data/ && bash task.sh | ||
``` | ||
|
||
看起来没有问题,但是任务就是无法执行。 | ||
起初我以为我的Cron命令写的有问题,反复比较之后,确定命令没有问题。 | ||
通过Google一番,很多答案都说是cron服务可能有问题,遂去查看。 | ||
|
||
``` | ||
# 查看cron服务状态 | ||
service crond status | ||
# 状态输出 | ||
● crond.service - Command Scheduler | ||
Loaded: loaded (/usr/lib/systemd/system/crond.service; enabled; vendor preset: enabled) | ||
Active: active (running) since Fri 2019-11-15 15:51:55 CST; 5 days ago | ||
``` | ||
服务是正常的,所以排除了cron服务的问题。 | ||
|
||
再去看cron服务的日志 | ||
|
||
``` | ||
Nov 15 04:00:01 VM_0_8_centos CROND[5672]: (root) CMD (cd /data/Blog_Data/ && ./task.sh) | ||
``` | ||
看起来也是正常,没有报错信息,看起来也正常执行了。 | ||
|
||
到这里我就有点懵逼了,为什么没有反应?既没有报错,但是也没有正常执行。 | ||
冷静下来想,肯定是哪里错了,但是一时半会也没有解决问题思路,抱着试一试的态度,给cron新增了日志记录,并指定了输出目录,如下: | ||
|
||
``` | ||
0 */2 * * * cd /data/Blog_Data/ && bash task.sh >> /data/log/spider/mylog.log 2>&1 | ||
``` | ||
这回运行,看到了报错信息: | ||
``` | ||
# XXX是我线下数据库的表名 | ||
psycopg2.OperationalError: FATAL: database "XXX" does not exist | ||
``` | ||
恍然发现,原来是cron在执行虚拟环境中的任务时,没有正确读取环境变量。 | ||
|
||
至此,问题就比较清楚了,google在cron中配置环境变量的方式。 | ||
stackoverflow 上有答案给出了好几种配置的方式,我选择了一种我比较能够理解的方式。 | ||
stackoverflow 上该问题地址:https://stackoverflow.com/questions/2229825/where-can-i-set-environment-variables-that-crontab-will-use | ||
|
||
11月25日更新 | ||
后来又去研究了一下Linux上环境变量的配置问题,我下面这种操作方式,相当于临时新建了环境变量,在任务执行完成之后,即会被丢弃。个人更喜欢这种操作方式。 | ||
|
||
``` | ||
#!/bin/sh | ||
export FLASK_ENVIRONMENT=production | ||
export FLASK_ENV=production | ||
source data_venv/bin/activate | ||
python spider.py | ||
python parser.py | ||
deactivate | ||
``` | ||
|
||
配置完成后,发现任务可以正常执行了。问题得到解决! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
## 环境变量是什么? | ||
|
||
环境变量就是告诉程序在运行时应该以何种姿势运行,比如程序运行的目录,运行的环境,甚至是规定shell命令的颜色等等。 | ||
|
||
合理配置环境变量是程序正确运行的前提。 | ||
不依赖环境变量运行的程序,无需配置环境变量 | ||
|
||
### 环境变量应该怎么配置 | ||
|
||
从大的方面来讲,环境变量分为三类: | ||
|
||
一: 系统级的环境变量 | ||
针对所有用户都生效 | ||
|
||
二: 用户级别的环境变量 | ||
只对当前登录的用户生效 | ||
|
||
三: 临时环境变量 | ||
只对当前登录的shell生效,退出时失效。 | ||
|
||
### 这么多环境变量文件,究竟该配置哪一个 | ||
|
||
``` | ||
# 这几个文件都可以用来配置环境变量 | ||
etc/profile | ||
etc/bashrc | ||
~/.bash_profile | ||
~/.bashrc | ||
``` | ||
|
||
答案:建议优先配置 ~/.bashrc 文件,原因如下: | ||
|
||
1. 首先etc目录下的文件是用来修改系统级别的配置,会影响所有用户的行为,如非必要,不建议修改此处的内容。 | ||
|
||
2. 通常用到~/bash_profile的时候,会自动读取~/.bashrc。 | ||
|
||
解释: profile和bashrc分别对应两种使用shell的模式,分别是login-shell和non-login-shell。login-shell指当用户输入密码登陆的操作,比如打开Mac,输入账号密码,进入系统。non-login-shell指用户不需要有输入密码的操作,比如打开终端。 | ||
|
||
两种不同的模式调用环境变量的顺序是不同的。 | ||
``` | ||
# login-shell 读取顺序: | ||
etc/profile -> ~/.profile -> ~/.bashrc -> /etc/bashrc | ||
# non-login-shell 读取顺序: | ||
~/.bashrc -> /etc/bashrc | ||
``` | ||
参考资料:https://www.cnblogs.com/Monitor/p/4020223.html, | ||
https://blog.csdn.net/ZoeYen_/article/details/78560905 | ||
|
||
|
||
### Linux下的环境变量配置 | ||
|
||
系统级环境变量 | ||
``` | ||
vim /etc/profile | ||
# 在里面加入(以Flask启动需要的环境变量为例,下同): | ||
export FLASK_ENV=production | ||
source /etc/profile | ||
``` | ||
|
||
用户级别的环境变量 | ||
|
||
``` | ||
vim ~/.bashrc | ||
# 在里面加入: | ||
export FLASK_ENV=production | ||
# 立即生效 | ||
source ~/.bashrc | ||
``` | ||
|
||
临时环境变量 | ||
``` | ||
export FLASK_ENV=production | ||
``` | ||
|
||
### Mac OS下的环境变量配置 | ||
|
||
Mac OS 环境变量配置和 Linux 略有不同。 | ||
|
||
当 ./bash_profile 存在时,便不会继续读取 ~/.bashrc。因此,一般情况下只需要配置./bash_profile 即可。 | ||
|
||
``` | ||
# 用户级别的环境变量 | ||
vim ~/.bash_profile | ||
# 在里面加入: | ||
export FLASK_ENV=production | ||
# 立即生效 | ||
source ~/.bash_profile | ||
``` | ||
|
||
|
||
|
||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
### Pyppeteer 是什么? | ||
|
||
讨论 Pyppeteer 需要先来了解 Puppeteer。 | ||
|
||
Puppeteer 是谷歌发布的一款“控制” Chrome 和 Chromium 的开发工具,基于 NodeJS 开发。主要的应用场景是自动化测试。 | ||
|
||
额外多说一句,自动化测试和爬虫真是一队好基友,能用来做测试的工具,也基本能用来开发爬虫。 | ||
|
||
Pyppeteer 是 Puppeteer 的 Python 非官方实现,造福了广大 Python 测试(爬虫)开发者。 | ||
|
||
### 与 Selenium 相比,Pyppeteer 的优势是什么? | ||
|
||
一句话总结:不容易被不容易被反爬识别。 | ||
|
||
Selenium 久经考验,已经发展的比较成熟,应用也比较广泛。但是,Selenium的反爬也很成熟了,很容易被目标网站识别,比如淘宝、美团等。 | ||
|
||
Selenium 是通过 Webdriver 来操作浏览器,在操作浏览器进行访问、点击等行为时,都会留下痕迹,进而被反爬虫所识别。尽管可以通过 JS 注入的方式,来屏蔽这些特征,但是配置复杂,且不一定能解决问题。 | ||
|
||
Pyppeteer 作为一个更新的工具,网站反爬对他的防范程度较低,用来爬取数据,可以避免很多麻烦。尽管它也需要通过JS 注入来实现对网站反爬的屏蔽,但是需要配置的地方比较少,一次配置,全局生效,相对更加容易。 | ||
|
||
### 个人使用Pyppeteer的若干坑记录 | ||
|
||
1. 异步编程带来的复杂性 | ||
|
||
首先,Puppeteer 基于异步进行开发,Pyppeteer 也是。这意味着爬虫需用通过异步的方式来写,对于不太熟悉异步编程的开发者来讲,还是上手的难度。 | ||
|
||
其次,在 Python 中, async/await 的写法,对原有的代码侵入性比较大,需要修改的地方比较多。 | ||
|
||
再次,由于 Pyppeteer 本身问题比较多,异步编程又额外带来了复杂性,使得程序调试困难,出了问题排查困难。 | ||
|
||
2. Chromium 版本问题: | ||
|
||
Pyppeteer 在第一次运行时,会自动下载 Chromium,会非常慢。所以需要手动去下载。下载最新版之后,发现报错,运行不了。后来才发现 Pyppeteer 需要指定版本的 Chromium,版本号为: 575458 ,请各位注意。 | ||
|
||
3. 不稳定: | ||
|
||
Connection is closed 简直是噩梦,即便是按照网上的说法配置,也无法完全避免。 | ||
|
||
4. 更新缓慢: | ||
|
||
Pyppeteer 库基本上已经不更新了,遗留了大量的问题待解决,也无法适用于最新版本的 Chromium 。总之问题较多,使用 Pyppeteer 大规模的爬取数据,还不太现实。 | ||
|
||
### Pyppeteer的最佳实践姿势 | ||
|
||
最佳使用场景:当目标网站使用加密 Cookie 验证方式请求数据(含模拟登陆),且加密过程复杂,期望避免破解 JS 且快速得到 Cookie时,使用 Pyppeteer 是最佳选择。 | ||
|
||
个人的使用体会是用 Pyppeteer 直接去爬取数据,爬取比较慢且容易报错。整体来说很难保证爬取的效率。所以放弃用直接 Pyppeteer 爬数据的想法。改用 Pyppeteer 来获取 Cookie,然后用 Requests + Cookie 的方式去获取数据。 | ||
|
||
### 一个完整的获取Cookie的例子 | ||
|
||
背景描述: | ||
|
||
我需要爬取一个网站,该网站在第一次访问时,会通过 JS 生成一串加密后的 Cookie,其他的数据通过api携带此 Cookie 返回数据,无 Cookie 则无法正常返回数据。该网站 JS生成 Cookie 的方式破解起来很麻烦,所以考虑直接通过 Pyppeteer拿到 Cookie 了事。下面是一个例子,传入 URL 和 代理 IP 即可。 | ||
|
||
``` | ||
""" | ||
Description: | ||
Author:qxy | ||
Date: 2019/11/21 4:28 下午 | ||
File: get_cookie | ||
""" | ||
import asyncio | ||
from pyppeteer import launch | ||
import time | ||
import shutil | ||
async def get_cookie(url, ip): # 启动浏览器 | ||
browser = await launch({ | ||
'devtools': False, # 是否打开开发者模式 | ||
'userDataDir': './userdata', # 在目录下生成cookie文件,下次启动 Chromium 会自动携带已存储的cookie信息 | ||
"headless": True, # 打开无头模式,注意:在服务器上运行时,devtools也需要关闭 | ||
'args': [ | ||
'--disable-extensions', | ||
'--disable-bundled-ppapi-flash', | ||
'--mute-audio', | ||
'--no-sandbox', | ||
'--disable-setuid-sandbox', | ||
'--disable-infobars', | ||
'--proxy-server={}'.format(ip), # 配置代理ip | ||
]}) | ||
page = await browser.newPage() | ||
await page.setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " | ||
"(KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36") | ||
await page.setViewport({'width': 1080, 'height': 960}) | ||
await page.goto(url, timeout=100000) | ||
cookie_list = await page.cookies() | ||
cookies = '' | ||
for cookie in cookie_list: | ||
coo = "{}={};".format(cookie.get('name'), cookie.get('value')) | ||
cookies += coo | ||
print(cookies) | ||
time.sleep(5) | ||
await browser.close() | ||
return cookies | ||
``` |
File renamed without changes.
Empty file.