Skip to content

Commit

Permalink
文章项目
Browse files Browse the repository at this point in the history
上传原有文章 update 2
  • Loading branch information
qixiangyang committed Nov 29, 2019
1 parent f7289ec commit e909a92
Show file tree
Hide file tree
Showing 5 changed files with 264 additions and 0 deletions.
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
```

配置完成后,发现任务可以正常执行了。问题得到解决!
94 changes: 94 additions & 0 deletions 2019.11/2019_11_21_Linux下的环境变量究竟该怎么配置.md
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
```






97 changes: 97 additions & 0 deletions 2019.11/2019_11_25_Pyppeteer使用记录.md
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
```
Empty file.

0 comments on commit e909a92

Please sign in to comment.