前几天我总结了一下使用 nodejs 写爬虫的一些常用的技巧,并且表示写爬虫没有最好的语言,只有最合适的,选择自己最熟悉的最顺手的语言最好。然而,最近在爬取一些数据的过程中需要处理 HTML 表格,虽然可以自己手动处理,但是有现成的轮子干嘛不用?写爬虫就是为了获取数据,经过数据的分析处理得到有趣的结论,为了能够尽快的得到结论,根据场景选择合适编程语言就显得很重要,尤其当所需要的工具是语言独占的时候(其他语言也许有移植版,但是社区活跃度显然没有原版高),比如说 pandas

想把表格数据转换成 dataframe 只需要一句话:

# 引入 pandas
import pandas as pd
# 简单的一句话,即可将 html 转换成含有 dataframe 的列表
df_list = pd.read_html(html)
# 对每个 dataframe 也就是每张表格做处理
for df in df_list:
    print(df)

pandas 的这个功能真的深深的戳中了我的痛点,于是把整个爬虫用 python 重新写了一遍,这里也和上一次总结 nodejs 一样,总结一下 python 写爬虫的常用方法,方便以后复用,提高效率。

传送门:用 nodejs 写爬虫的常用技巧pandas 快速入门

注:本文代码均基于 python3.6.5 版本,使用其他版本有可能导致运行错误。

请求网页方法

python 请求网页一般会使用 requests 这个库,正如其介绍的那样:built for human beings。相当的通俗易懂,简单易用。

import requests

# get 请求
r = requests.get(url)
# 带参数
payload = {
    'key': 'value'
}
r = requests.get(url, params=payload)

# post 请求
r = requests.post(url)
# 带 form 参数
form_data = {
    'key': 'value'
}
r = requests.post(url, data=form_data)
# 带 json 参数
json_data = {
    'key': 'value'
}
r = requests.post(url, json=json_data)

伪装浏览器与串改请求头

其实伪装浏览器的本质就是修改请求头(request header),来让服务器难以辨别请求是来自爬虫还是真实用户的浏览器。通常设置 User-Agent 即可应对大部分网站。

headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36'
}
r = request.get(url, headers=headers)

获取和设置 Cookies

这里就涉及到 requests 发送请求后的返回值了,也就是上面例子中的 r。用户存储在浏览器的 Cookies 通常是浏览器根据返回头(response header)中的 Set-Cookie 的值来设置的,在 python 中这样获取:

r = request.get(url)
cookie = r.headers.get('set-cookie').split(';')[0].strip()

取消自动重定向

nodejs 的技巧文章中解释过,自动重定向可能会使用不正确的 Cookie 导致结果永远不对,因此有时需要取消这个功能:

r = requests.get(url, allow_redirects=False)

页面解析

我喜欢使用 beautifulsoup

from bs4 import BeautifulSoup
r = requests.get(url)
# 这个 html.parser 是解析器,你也可以设置其他的,比如:lxml,如果你要解析 XML 文档,那么你就要把它设置为:lxml-xml。当然使用前要安装 lxml。
soup = BeautifulSoup(r.text, 'html.parser')
# 例:找到并打印页面中的所有超链接
for item in soup.find_all('a'):
    print('{} - {}'.format(item.get_text(), item.get('href')))

使用代理

难免的情况,不论是 IP 被封,还是外网无法访问,均要使用代理来绕过限制:

教你科学上网:正确的科学上网方式

proxies = {
    'http': 'http://127.0.0.1:6153/', 
    'https': 'http://127.0.0.1:6154/', 
}
# 简直是直观的不能再直观了
r = requests.get(url, proxies=proxies)

多线程并行爬取

看了 nodejs 版本的朋友需要注意下,这里是多线程了,不是多进程。这里就提供一个线程池的模板:

import queue
import time
import threading

# 任务队列
q = queue.Queue()
# 线程数
THREADS = 4
# 任务列表
JOBS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# 单个任务的处理方法
def run(job):
	print(job)

# 线程方法,不断的从任务队列中取出参数并执行处理方法
def thread_function():
	while True:
		job = q.get()
		run(job)
		time.sleep(1)
		q.task_done()

# 开启 THREADS 个线程
for i in range(THREADS):
	t = threading.Thread(target=thread_function)
	t.setDaemon(True)
	t.start()

# 把任务添加进队列
for param in JOBS:
	q.put(param)

# 等待任务队列结束
q.join()

python 爬虫我写的不多,本文只能分享这些基本常用的方法,日后有更多经验再来补充~