python爬虫总结

用Python开发爬虫是一件很轻松愉悦的事情,因为其相关库较多,而且使用方便,短短十几行代码就可以完成一个爬虫的开发;
但是,在应对具有反爬措施的网站,使用js动态加载的网站,App采集的时候就得动动脑子了;并且在开发分布式爬虫,高性能爬虫的时候更得用心设计。

Python开发爬虫常用的工具总结

  1. reqeusts:Python HTTP网络请求库;
  2. pyquery: Python HTML DOM结构解析库,采用类似JQuery的语法;
  3. BeautifulSoup:python HTML以及XML结构解析;
  4. selenium:Python自动化测试框架,可以用于爬虫;
  5. phantomjs:无头浏览器,可以配合selenium获取js动态加载的内容;
  6. re:python内建正则表达式模块;
  7. fiddler:抓包工具,原理就是是一个代理服务器,可以抓取手机包;
  8. anyproxy:代理服务器,可以自己撰写rule截取request或者response,通常用于客户端采集;
  9. celery:Python分布式计算框架,可用于开发分布式爬虫;
  10. gevent:Python基于协程的网络库,可用于开发高性能爬虫
  11. grequests:异步requests
  12. aiohttp:异步http client/server框架
  13. asyncio:python内建异步io,事件循环库
  14. uvloop:一个非常快速的事件循环库,配合asyncio效率极高
  15. concurrent:Python内建用于并发任务执行的扩展
  16. scrapy:python 爬虫框架;
  17. Splash:一个JavaScript渲染服务,相当于一个轻量级的浏览器,配合lua脚本通过他的http API 解析页面;
  18. Splinter:开源自动化Python web测试工具
  19. pyspider:Python爬虫系统

网页抓取思路

  1. 数据是否可以直接从HTML中获取?数据直接嵌套在页面的HTML结构中;
  2. 数据是否使用JS动态渲染到页面中的?数据嵌套在js代码中,然后采用js加载到页面或者采用ajax渲染;
  3. 获取的页面使用是否需要认证?需要登录后页面才可以访问;
  4. 数据是否直接可以通过API得到?有些数据是可以直接通过api获取到,省去解析HTML的麻烦,大多数API都是以JSON格式返回数据;
  5. 来自客户端的数据如何采集?例如:微信APP和微信客户端

如何应对反爬

  1. 不要太过分,控制爬虫的速率,别把人家整垮了,那就两败俱伤了;
  2. 使用代理隐藏真实IP,并且实现反爬;
  3. 让爬虫看起来像人类用户,选择性滴设置以下HTTP头部:
    • Host:https://www.baidu.com
    • Connection:keep-alive
    • Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8
    • UserAgent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.104 Safari/537.36
    • Referer: http://s.weibo.com/user/gamelife1314&Refer=index
    • Accept-Encoding: gzip, deflate
    • Accept-Language: zh-CN,zh;q=0.8
  4. 查看网站的cookie,在某些情况下,请求需要添加cookie用于通过服务端的一些校验;

案例说明

静态页面解析(获取微信公众号文章)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import pyquery
import re


def weixin_article_html_parser(html):
"""
解析微信文章,返回包含文章主体的字典信息
:param html: 文章HTML源代码
:return:
"""

pq = pyquery.PyQuery(html)

article = {
"weixin_id": pq.find("#js_profile_qrcode "
".profile_inner .profile_meta").eq(0).find("span").text().strip(),
"weixin_name": pq.find("#js_profile_qrcode .profile_inner strong").text().strip(),
"account_desc": pq.find("#js_profile_qrcode .profile_inner "
".profile_meta").eq(1).find("span").text().strip(),
"article_title": pq.find("title").text().strip(),
"article_content": pq("#js_content").remove('script').text().replace(r"\r\n", ""),
"is_orig": 1 if pq("#copyright_logo").length > 0 else 0,
"article_source_url": pq("#js_sg_bar .meta_primary").attr('href') if pq(
"#js_sg_bar .meta_primary").length > 0 else '',

}

# 使用正则表达式匹配页面中js脚本中的内容
match = {
"msg_cdn_url": {"regexp": "(?<=\").*(?=\")", "value": ""}, # 匹配文章封面图
"var ct": {"regexp": "(?<=\")\d{10}(?=\")", "value": ""}, # 匹配文章发布时间
"publish_time": {"regexp": "(?<=\")\d{4}-\d{2}-\d{2}(?=\")", "value": ""}, # 匹配文章发布日期
"msg_desc": {"regexp": "(?<=\").*(?=\")", "value": ""}, # 匹配文章简介
"msg_link": {"regexp": "(?<=\").*(?=\")", "value": ""}, # 匹配文章链接
"msg_source_url": {"regexp": "(?<=').*(?=')", "value": ""}, # 获取原文链接
"var biz": {"regexp": "(?<=\")\w{1}.+?(?=\")", "value": ""},
"var idx": {"regexp": "(?<=\")\d{1}(?=\")", "value": ""},
"var mid": {"regexp": "(?<=\")\d{10,}(?=\")", "value": ""},
"var sn": {"regexp": "(?<=\")\w{1}.+?(?=\")", "value": ""},
}
count = 0
for line in html.split("\n"):
for item, value in match.items():
if item in line:
m = re.search(value["regexp"], line)
if m is not None:
count += 1
match[item]["value"] = m.group(0)
break
if count >= len(match):
break

article["article_short_desc"] = match["msg_desc"]["value"]
article["article_pos"] = int(match["var idx"]["value"])
article["article_post_time"] = int(match["var ct"]["value"])
article["article_post_date"] = match["publish_time"]["value"]
article["article_cover_img"] = match["msg_cdn_url"]["value"]
article["article_source_url"] = match["msg_source_url"]["value"]
article["article_url"] = "https://mp.weixin.qq.com/s?__biz={biz}&mid={mid}&idx={idx}&sn={sn}".format(
biz=match["var biz"]["value"],
mid=match["var mid"]["value"],
idx=match["var idx"]["value"],
sn=match["var sn"]["value"],
)

return article


if __name__ == '__main__':

from pprint import pprint
import requests
url = ("https://mp.weixin.qq.com/s?__biz=MzI1NjA0MDg2Mw==&mid=2650682990&idx=1"
"&sn=39419542de39a821bb5d1570ac50a313&scene=0#wechat_redirect")
pprint(weixin_article_html_parser(requests.get(url).text))

# {'account_desc': '夜听,让更多的家庭越来越幸福。',
# 'article_content': '文字:安梦 \xa0 \xa0 声音:刘筱 得到了什么?又失去了什么?',
# 'article_cover_img': 'http://mmbiz.qpic.cn/mmbiz_jpg/4iaBNpgEXstYhQEnbiaD0AwbKhmCVWSeCPBQKgvnSSj9usO4q997wzoicNzl52K1sYSDHBicFGL7WdrmeS0K8niaiaaA/0?wx_fmt=jpeg',
# 'article_pos': 1,
# 'article_post_date': '2017-07-02',
# 'article_post_time': 1499002202,
# 'article_short_desc': '周日 来自刘筱的晚安问候。',
# 'article_source_url': '',
# 'article_title': '【夜听】走到这里',
# 'article_url': 'https://mp.weixin.qq.com/s?__biz=MzI1NjA0MDg2Mw==&mid=2650682990&idx=1&sn=39419542de39a821bb5d1570ac50a313',
# 'is_orig': 0,
# 'weixin_id': 'yetingfm',
# 'weixin_name': '夜听'}
使用phantomjs解析js渲染的页面–微博搜索

有些页面采用复杂的js逻辑处理,包含各种Ajax请求,请求之间还包含一些加密操作,通过分析js逻辑重新渲染页面拿到
想要的数据可谓比登天还难,没有坚实的js基础,不熟悉各种js框架,搞明白这种页面就别想了;
采取类似浏览器的方式渲染页面,直接获取页面HTML方便多了。

例如:http://s.weibo.com/ 搜索出来的结果是使用js动态渲染的,直接获取HTML并不会得到搜索的结果,所以我们要运行
页面中的js,将页面渲染成功以后,再获取它的HTML进行解析;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#!/usr/bin/env python3
# encoding: utf-8
import time
from urllib import parse

from selenium import webdriver
from selenium.common.exceptions import TimeoutException, WebDriverException
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from pyquery import PyQuery


def weibo_user_search(url: str):
"""通过phantomjs获取搜索的页面html"""

desired_capabilities = DesiredCapabilities.CHROME.copy()
desired_capabilities["phantomjs.page.settings.userAgent"] = ("Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/59.0.3071.104 Safari/537.36")
desired_capabilities["phantomjs.page.settings.loadImages"] = True
# 自定义头部
desired_capabilities["phantomjs.page.customHeaders.Upgrade-Insecure-Requests"] = 1
desired_capabilities["phantomjs.page.customHeaders.Cache-Control"] = "max-age=0"
desired_capabilities["phantomjs.page.customHeaders.Connection"] = "keep-alive"

driver = webdriver.PhantomJS(executable_path="/usr/bin/phantomjs", # 设置phantomjs路径
desired_capabilities=desired_capabilities,
service_log_path="ghostdriver.log",)
# 设置对象的超时时间
driver.implicitly_wait(1)
# 设置页面完全加载的超时时间,包括页面全部渲染,异步同步脚本都执行完成
driver.set_page_load_timeout(60)
# 设置异步脚本的超时时间
driver.set_script_timeout(60)

driver.maximize_window()
try:
driver.get(url=url)
time.sleep(1)
try:
# 打开页面之后做一些操作
company = driver.find_element_by_css_selector("p.company")
ActionChains(driver).move_to_element(company)
except WebDriverException:
pass
html = driver.page_source
pq = PyQuery(html)
person_lists = pq.find("div.list_person")
if person_lists.length > 0:
for index in range(person_lists.length):
person_ele = person_lists.eq(index)
print(person_ele.find(".person_name > a.W_texta").attr("title"))
return html
except (TimeoutException, Exception) as e:
print(e)
finally:
driver.quit()

if __name__ == '__main__':
weibo_user_search(url="http://s.weibo.com/user/%s" % parse.quote("新闻"))
# 央视新闻
# 新浪新闻
# 新闻
# 新浪新闻客户端
# 中国新闻周刊
# 中国新闻网
# 每日经济新闻
# 澎湃新闻
# 网易新闻客户端
# 凤凰新闻客户端
# 皇马新闻
# 网络新闻联播
# CCTV5体育新闻
# 曼联新闻
# 搜狐新闻客户端
# 巴萨新闻
# 新闻日日睇
# 新垣结衣新闻社
# 看看新闻KNEWS
# 央视新闻评论
使用Python模拟登陆获取cookie

有些网站比较蛋疼,通常需要登录之后才可以获取数据,下面展示一个简单的例子:用于登录网站吗,获取cookie,然后可以用于其他请求

但是,这里仅仅在没有验证码的情况下,如果要有短信验证,图片验证,邮箱验证那就要另行设计了;

目标网站:http://www.newrank.cn,日期:2017-07-03,如果网站结构更改,就需要修改代以下码了;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#!/usr/bin/env python3
# encoding: utf-8

from time import sleep
from pprint import pprint

from selenium.common.exceptions import TimeoutException, WebDriverException
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium import webdriver


def login_newrank():
"""登录新榜,获取他的cookie信息"""

desired_capabilities = DesiredCapabilities.CHROME.copy()
desired_capabilities["phantomjs.page.settings.userAgent"] = ("Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/59.0.3071.104 Safari/537.36")
desired_capabilities["phantomjs.page.settings.loadImages"] = True

# 自定义头部
desired_capabilities["phantomjs.page.customHeaders.Upgrade-Insecure-Requests"] = 1
desired_capabilities["phantomjs.page.customHeaders.Cache-Control"] = "max-age=0"
desired_capabilities["phantomjs.page.customHeaders.Connection"] = "keep-alive"

# 填写自己的账户进行测试
user = {
"mobile": "user",
"password": "password"
}

print("login account: %s" % user["mobile"])

driver = webdriver.PhantomJS(executable_path="/usr/bin/phantomjs",
desired_capabilities=desired_capabilities,
service_log_path="ghostdriver.log", )

# 设置对象的超时时间
driver.implicitly_wait(1)
# 设置页面完全加载的超时时间,包括页面全部渲染,异步同步脚本都执行完成
driver.set_page_load_timeout(60)
# 设置异步脚本的超时时间
driver.set_script_timeout(60)

driver.maximize_window()

try:
driver.get(url="http://www.newrank.cn/public/login/login.html?back=http%3A//www.newrank.cn/")
driver.find_element_by_css_selector(".login-normal-tap:nth-of-type(2)").click()
sleep(0.2)
driver.find_element_by_id("account_input").send_keys(user["mobile"])
sleep(0.5)
driver.find_element_by_id("password_input").send_keys(user["password"])
sleep(0.5)
driver.find_element_by_id("pwd_confirm").click()
sleep(3)
cookies = {user["name"]: user["value"] for user in driver.get_cookies()}
pprint(cookies)

except TimeoutException as exc:
print(exc)
except WebDriverException as exc:
print(exc)
finally:
driver.quit()

if __name__ == '__main__':
login_newrank()
# login account: 15395100590
# {'CNZZDATA1253878005': '1487200824-1499071649-%7C1499071649',
# 'Hm_lpvt_a19fd7224d30e3c8a6558dcb38c4beed': '1499074715',
# 'Hm_lvt_a19fd7224d30e3c8a6558dcb38c4beed': '1499074685,1499074713',
# 'UM_distinctid': '15d07d0d4dd82b-054b56417-9383666-c0000-15d07d0d4deace',
# 'name': '15395100590',
# 'rmbuser': 'true',
# 'token': 'A7437A03346B47A9F768730BAC81C514',
# 'useLoginAccount': 'true'}

在获取cookie之后就可以将获得的cookie添加到后续的请求中了,但是因为cookie是具有有效期的,因此需要定时更新;
可以通过设计一个cookie池来实现,动态定时登录一批账号,获取cookie之后存放在数据库中(redis,MySQL等等),
请求的时候从数据库中获取一条可用cookie,并且添加在请求中访问;

使用pyqt5爬个数据试试(PyQt 5.9.2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import sys
import csv

import pyquery

from PyQt5.QtCore import QUrl
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWebEngineWidgets import QWebEngineView


class Browser(QWebEngineView):

def __init__(self):
super(Browser, self).__init__()
self.__results = []
self.loadFinished.connect(self.__result_available)

@property
def results(self):
return self.__results

def __result_available(self):
self.page().toHtml(self.__parse_html)

def __parse_html(self, html):
pq = pyquery.PyQuery(html)
for rows in [pq.find("#table_list tr"), pq.find("#more_list tr")]:
for row in rows.items():
columns = row.find("td")
d = {
"avatar": columns.eq(1).find("img").attr("src"),
"url": columns.eq(1).find("a").attr("href"),
"name": columns.eq(1).find("a").attr("title"),
"fans_number": columns.eq(2).text(),
"view_num": columns.eq(3).text(),
"comment_num": columns.eq(4).text(),
"post_count": columns.eq(5).text(),
"newrank_index": columns.eq(6).text(),
}
self.__results.append(d)

with open("results.csv", "a+", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=["name", "fans_number", "view_num", "comment_num", "post_count",
"newrank_index", "url", "avatar"])
writer.writerows(self.results)

def open(self, url: str):
self.load(QUrl(url))


if __name__ == '__main__':
app = QApplication(sys.argv)
browser = Browser()
browser.open("https://www.newrank.cn/public/info/list.html?period=toutiao_day&type=data")
browser.show()
app.exec_()

使用Fiddler抓包分析

Fiddler 原理其实是一个代理服务器,默认地址:127.0.0.1:8888,也会监听其他的网口,通过它的所有请求和响应都会被记录;

使用fiddler抓包主要是为了解决以下几个问题:

  1. 能更清楚滴查看请求的详细信息,使用浏览器调试工具也是可以的;
  2. 有时候有些请求是分布完成的,先有个预请求获取一些数据,然后附加到另一个请求中;
    例如搜索文章:http://weixin.sogou.com/weixin?type=2&query=微信
    然后使用搜索工具通过账号过滤的时候,其实他首先请求获取账号的微信号的openid
    最后,拼接成一个新的请求进行重定向;
  3. 对手机端的请求进行分析;
浏览器抓包

目的:观察浏览器一系列请求,用于分析请求之间的关系,用于请求重现;

  1. 安装Fiddler并且打开;
  2. 安装浏览器插件,Firefox安装插件:AutoProxy,设置代理;
    或者Windows系统设置系统代理:Internet选项->连接->局域网设置->代理服务器->填写端口:8888,地址:127.0.0.1->确定;
    设置代理
    浏览器设置代理只会抓取该浏览器访问的文章,如果设置系统代理,会抓取系统所有的请求;
  3. 查看结果,
    微信搜索文章展示
    微信搜索文章展示
fiddler手机抓包

目的:通过手机抓包可以看到隐藏在APP背后的API,对于APP采集有较大的帮助;

使用fiddler对手机进行抓包,手机必须和fiddler所在的主机同处一个网段,比如手机和电脑连接于同一个WiFi;
设置手机代理,前面说了fiddler除了监听本地换回网口(127.0.0.1)也会监听本机所有网口的我ip,可使用ipconfig命令查看本地地址;

查看本机IP

  1. 使用fiddler对手机抓包,需要对fiddler进行设置;
    设置fiddler

  2. 验证手机是否和电脑处于同一网段,手机打开浏览器访问:http://192.168.31.49:8888,如果出现以下界面,则证明可以;
    验证

  3. 手机设置代理
    手机设置代理
  4. 打开一个链接,就会看到fiddler中显示监听到的请求,如下图,不过我这里设置了过滤条件,只会看到相应的请求;
    手机抓包

使用anyproxy抓取客户端数据–客户端数据采集

前面说过,fiddler也是相当于一个代理服务器,而这里annproxy完全就是一个代理服务器,与之不同的是,anyproxy可以通过自定义Rule对每个请求进行转发,重置,丢弃,对于响应也可以进行自定义,类似于中间人攻击(MITM攻击);

有些时候,因为一些客户端请求,APP请求通过内部加密的形式,即使知道API,也极难复制,因此可以采取类似中间人攻击的方式,截取请求进行转发,或者获取响应进行处理;

例如:微信客户端或者微信APP获取微信公众号文章的阅读数和点赞数

举个例子,劫持微信客户端内公众号文章的请求;这里只是截取它的请求,可以直接从响应中获取点赞数和阅读数,也可以在拿到请求后
自己构造获取阅读数和点赞数的请求,下面只演示截取步骤。

  1. 参考官网安装anyproxynpm install -g anyproxy@beta
  2. 生成CA证书:anyproxy-ca,并且安装,手机端可以将证书发送到手机,然后通过手机自带的证书管理工具安装,电脑双击打开即可;
  3. 启动并且加载自定义rule:anyproxy --intercept --port 9001 --web 9002 --rule --rule request_hijacking.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    module.exports = {
    *beforeSendRequest(requestDetail) {
    if (requestDetail.url.indexOf('https://mp.weixin.qq.com') === 0 || requestDetail.url.indexOf('http://mp.weixin.qq.com') === 0) {
    var querystring = require('querystring');
    var data = {
    method: requestDetail.requestOptions.method,
    url: requestDetail.url,
    headers: querystring.stringify(requestDetail.requestOptions.headers),
    body: requestDetail.requestData.toString()
    };
    // 这里将打印该请求的相关数据
    console.log(data);
    // 由于这个请求中有些参数是客户端自己生成的,很难伪造,因此可以截取该请求的信息,使用程序自己进行仿造
    // 我这里使用tornado开发了一个service,用于接收请求的必要信息,然后使用程序进行二次模拟获取其他的数据
    // content = querystring.stringify(data);
    // var options = {
    // method: "post",
    // host: "114.215.110.xxx",
    // port: 8088,
    // path: "/wx-request-task",
    // headers: {
    // "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
    // "Content-Length": content.length
    // }
    // };
    // var http = require("http");
    // var req = http.request(options, function (res) {});
    // req.on('error', function (e) {
    // console.log('problem with request: ' + e.message);
    // });
    // req.write(content);
    // req.end();
    }
    return {
    requestOptions: requestDetail.requestOptions
    };
    },
    *beforeSendResponse(requestDetail, responseDetail) {
    if (requestDetail.url.indexOf('https://mp.weixin.qq.com') === 0 || requestDetail.url.indexOf('http://mp.weixin.qq.com') === 0) {
    console.log("hello world");
    // 这里针对微信文章可以自定义自己的逻辑
    return null;
    }
    }
    };
  4. 设置代理,设置方式同在fiddler中提到的一样;
    设置代理

  5. 微信客户端内打开一个URL:http://mp.weixin.qq.com/s/i-21YnGR_OoQZa8ZDzV4ng,查看anyproxy打印的结果
    截取结果
    截取结果

关于开发高性能爬虫的总结

爬虫是一个密集型网络I/O程序,每次任务的执行最多的消耗是在等待http响应,利用这个等待的时间做些其他的事情,就能够打到
优化程序的目的;平时总是会遇到一个任务需要n个步骤去完成,而且有不确定个任务等待执行,如果都采取串行的方式,那么要等到猴年马月了,针对这些情况,提出以下优化措施;

  1. 工欲善其事,必先利其器,可以对爬虫服务器进行一些配置,主要是针对网络进行优化;

    • 设置打开文件数目的最大限制,修改 /etc/security/limits.conf, 添加如下内容:

      * soft nofile 10240
      * hard nofile 10240
      * soft nproc 65530
      * hard nproc 65530
      
      关于此文件的修改,参考一下文档:
          http://gfllove.blog.163.com/blog/static/1515027200923010653628/
          http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=20671208&id=4935150
      
    • 在/etc/sysctl.conf中添加以下内容,然后sysctl -p:

      net.ipv4.tcp_syn_retries = 1
      net.ipv4.tcp_synack_retries = 1
      net.ipv4.tcp_keepalive_time = 600
      net.ipv4.tcp_keepalive_probes = 3
      net.ipv4.tcp_keepalive_intvl =15
      net.ipv4.tcp_retries2 = 5
      net.ipv4.tcp_fin_timeout = 2
      net.ipv4.tcp_max_tw_buckets = 36000
      net.ipv4.tcp_tw_recycle = 1
      net.ipv4.tcp_tw_reuse = 1
      net.ipv4.tcp_max_orphans = 32768
      net.ipv4.tcp_syncookies = 1
      net.ipv4.tcp_max_syn_backlog = 16384
      net.ipv4.tcp_wmem = 8192 131072 16777216
      net.ipv4.tcp_rmem = 32768 131072 16777216
      net.ipv4.tcp_mem = 786432 1048576 1572864
      net.ipv4.ip_local_port_range = 1024 65000
      net.core.somaxconn = 16384
      net.core.netdev_max_backlog = 16384
      
  2. 让程序并发(concurrency)起来,利用前面提到的:gevent,grequests,aiohttp,asyncio,uvloop让程序并发运行,利用等待http响应的时间;
    并发下载的例子:Python并发下载的例子和比较

  3. 让程序并行(parallellism)起来,利用现代系统多核的优势,采用多进程的方式给爬虫提速;