《Python网络数据采集》记录

简介

最近读了这本书,做些记录。本书用的是Python3,我习惯用2,有些地方不能用2再用3。有些章节对我作用不大,或者只是一个较粗略的介绍,会略过不记。

创建爬虫

解析HTML

urllib与urllib2

在很多地方看到urllib和urllib2互用urlopen,但实际上urllib2是urllib的进一步封装,在很多地方也有区别:

  • urllib2可以接受一个Request类的实例来设置URL请求的headers,urllib仅可以接受URL。这意味着,你不可以通过urllib模块伪装你的User Agent字符串等(伪装浏览器)。
  • urllib提供urlencode方法用来GET查询字符串的产生,而urllib2没有。这是为何urllib常和urllib2一起使用的原因。
  • urllib2模块比较优势的地方是urlliburllib2.urlopen可以接受Request对象作为参数,从而可以控制HTTP Request的header部。
  • 但是urllib.urlretrieve函数以及urllib.quote等一系列quote和unquote功能没有被加入urllib2中,因此有时也需要urllib的辅助。

BeautifulSoup

bs0bj = BeautifulSoup(html.read())获得一个BS对象后,可以

  • 通过结点访问,如:

    1
    2
    3
    4
    5
    6
    bs0bj.h1
    bs0bj.html.body.h1
    bs0bj.body.h1

    获取属性:
    bs0bj.h1.attrs["src"]
  • 通过tag的属性

    1
    2
    bs0bj.findAll("table")[4].findAll("tr")[2].find("td")
    bs0bj.findAll("span", {"class": "green"}).get_text()

findAll与find

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
findAll(tag, attributes, recursive, text, limit, keywords)
find(tag, attributes, recursive, text, keywords)
recursive若为True,则不论层数查找;若为False,则查找一级标签
text用文本内容匹配
limit为查找前n个结果

keyword的功能有些冗余,下面两行代码完全一样:
bs0bj.findAll(class_="text")
bs0bj.findAll("", {"class": "text"})

结合正则表达式re:
bs0bj.findAll("img", {"src": re.compile("\.\.\/img\/gifts\/img.*\.jpg")})

结合Lambda表达式:
bs0bj.findAll(lambda tag: len(tag.attrs) == 2)

  • 通过导航树
    1
    2
    3
    4
    5
    6
    7
    8
    .children(一级标签)
    .descendants(所有后代)
    .next_sibling(同级之后单个标签)
    .next_siblings(同级之后所有标签)
    .previous_sibling(同级之前单个标签)
    .previous_siblings(同级之后所有标签)
    .parent(父标签)
    .parents(父标签)

递归爬取(示例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from urllib.request import urlopen
from bs4 import BeautifulSoup
import re

pages = set()
def getLinks(pageUrl):
global pages
html = urlopen("http://en.wikipedia.org"+pageUrl)
bs0bj = BeautifulSoup(html)
for link in bs0bj.findAll("a", href=re.compile("^(/wiki/)")):
if 'href' in link.attrs:
if link.attrs['href'] not in pages:
# 我们遇到了新页面
newPage = link.attrs['href']
print(newPage)
pages.add(newPage)
getLinks(newPage)

getLinks("")

存储数据

下载

urllib.urlretrieve(url, path)将url下载到本地path下

CSV

1
2
3
4
5
6
7
8
9
import csv
csvFile = open("../files/test.csv", 'w+')
try:
writer = csv.writer(csvFile)
writer.writerow(('number', 'number plus 2', 'number times 2'))
for i in range(10):
writer.writerow((i, i+2, i*2))
finally:
csvFile.close()

MySQL

  • 通过创建cursor来填充操作数据库的字符串

    1
    2
    3
    conn = _mysql.connect()
    cur = conn.cursor()
    cur.execute(SQL)
  • 通过query()

    1
    2
    db = _mysql.connect()
    db.query(SQL)

db.query()是没有游标cursor时,用来直接执行SQL语句,该类主要逻辑就是,将参数填入SQL语句格式中作为字符串形式的SQL语句,之后用query()执行语句。

读取文档

纯文本

str: str(text, 'uf8-8')使用uf8-8编码
bytes: 先用bytes()bytes转化成str

1
2
content = bytes(content, "UTF-8")
content = content.decode("UTF-8")

CSV

比较方便的做法是从网上直接把文件读成一个字符串,然后转换成一个StringIO对象,使它具有文件的属性。

1
2
3
4
5
6
7
8
9
10
from urllib import urlopen
from io import StringIO
import csv
data = urlopen("http://pythonscraping.com/files/MontyPythonAlbums.csv").read().decode('ascii', 'ignore')
dataFile = StringIO(data)
csvReader = csv.reader(dataFile)
# csvReader = csv.DictReader(dataFile)

for row in csvReader:
print(row)

高级数据采集

数据清洗

re.sub(查找字符串, 替换字符串, 文本)str.replace(查找字符串, 替换字符串)(不支持正则)

自然语言处理

  • n-gram模型
    将文本滚动分割出n个单词作为一组,计算所有组的频率,选取出包含该组词汇的句子。

  • 马尔可夫模型
    后一单词出现的频率只与前一个单词有关,每一个单词构造一个字典,计算所有频次。选取起始词,构造随机数,在该词字典中选中后一单词,以此类推。

  • nltk语言包

    穿越网页表单与登录窗口进行采集

  • 使用Requests库进行POST请求

    1
    2
    3
    4
    5
    import requests

    params = {'firstname': 'Ryan', 'lastname': 'Mitchell'}
    r = requests.post("http://pythonscraping.com/files/processing.php", data=params)
    print r.text

单选按钮、复选框和其他输入一样,只要获取字段和值即可。

上传文件:将open(path)对象作为字段的值

  • 进行cookie

    1
    2
    3
    4
    5
    6
    7
    8
    9
    import requests

    session = requests.Session()

    params = {'username': 'username', 'password': 'password'}
    s = session.pos("http://pythonscraping.com/pages/cookies/welcome.php", params)
    print(s.cookies.get_dict())
    s = session.get("http://pythonscraping.com/pages/cookies/profile.php")
    print(s.text)
  • 进行接入认证

    1
    2
    3
    4
    5
    6
    7
    import requests
    from requests.auth import AuthBase
    from requests.auth import HTTPBasicAuth

    auth = HTTPBasicAuth('ryan', 'password')
    r = requests.post(url="http://pythonscraping.com/pages/auth/login.php", auth=auth)
    print(r.text)

采集JavaScript

通过Selenium绑定PhantomJS访问网页,执行JavaScript

图像识别与文字处理

通过Tesseract训练验证码

一分一毛也是心意