Python爬虫

[TOC]

Requests入门使用

Requests中文编码问题

Requests库

1
resp = requests.get(url, params={}, headers={})
  • 使用get方式发起对一个页面的访问请求,对requests方法二次封装。
  • params:用字典的形式来传参,可以动态地修改请求参数,输出结果和直接在url上拼接是一样的。
  • headers:用字典的形式来添加请求头信息,如”user-agent”、”cookie”等登录信息。
  • 返回response对象,包含了爬虫返回的全部内容。

Response对象

1
2
3
4
5
r.status_code # HTTP请求返回的状态码
r.encoding # 响应头中content-type中charset值,若不存在则为'ISO-8859-1'
r.apparent_encoding # 从内容中分析出的响应内容编码格式,需要消耗计算资源
r.text # HTTP响应内容的字符串形式
r.content # HTTP响应内容的二进制形式
  • 若返回内容中包含乱码,需要重新设置r.encoding。常用r.encoding = r.apparent_encoding。

bs4库

用来解析各种标签树的库

基本元素

  • 标签:
  • 标签的名字:.name
  • 标签的属性:.attrs,返回字典类型。
  • 标签内非属性字符串:
    • .string,若有多个则返回none。
    • .text,若有多个则连接后返回str。

遍历元素

下行遍历

  • .contents:返回子结点的列表,也包含字符串类型如’\n’、’\r’等。
  • .children:返回子结点的迭代类型
  • .descendants:返回子孙结点的迭代类型

上行遍历

  • .parent:返回父亲标签
  • .parents:返回先辈标签的迭代类型

平行遍历(同一个父节点下,包含字符串类型)

  • .next_sibling:返回按照HTML文本顺序的下一个平行结点标签

  • .previous_sibling:返回按照HTML文本顺序的上一个平行结点标签

  • .next_siblings:返回后续所有平行结点的标签的迭代类型

  • .previous_siblings:返回前续所有平行结点的标签的迭代

soup属性

1
2
3
from bs4 import BeautifulSoup
soup = BeautifulSoup(mk, 'html.parser')
soup = BeautifulSoup(mk, 'xml') # 需要pip install lxml

可以用soup来获取该标签的第一个

1
2
3
soup.title
soup.a
......

soup函数

1
2
lis = soup.find_all(name, attrs, recursive, string, **kwargs)
string = soup.find() # 参数同上
  • name:标签名的检索字符串或字符串列表,可以传入正则表达式
  • attrs:标签属性值的检索字符串或字典
  • recursive:默认为True,False表示只在根节点的儿子结点中找。
  • string:要检索的非属性的字符串值

re正则表达式库

常用正则表达式

语法

正则表达式语法由字符操作符构成。

  • 单个字符

    • .表示任意单个字符。

    • [abc]表示a、b、c,[a-z]表示a-z的单个字符,对单个字符给出取值范围

    • [^abc]表示非a非b非c的单个字符,对单个字符给出排除范围
    • \d表示单个数字,等价于[0-9]。\w表示单个单词字符,等价于[A-Za-z0-9_]。
  • 字符扩展
    • *表示前一个字符0或无限次扩展,+表示前一个字符1或无限次扩展,?表示前一个字符0或1次扩展。
    • {m}扩展前一个字符m次,{m, n}扩展前一个字符m至n次。
  • 最小匹配

    • *?+???{m, n}?
    • 当一个操作符可以匹配不同长度的时候,可以在这个操作符后面加一个?来获得最小匹配的结果。
  • 其他

    • ^abc表示abc且在一个字符串的开头,abc$表示abc且在一个字符串的结尾。
    • (abc|def)表示abc、def,左右表达式任意一个。

经典正则表达式

  • 由26个字母组成的字符串:^[A-Za-z]+$
  • 由26个字母和数字组成的字符串:^[A-Za-z0-9]+$
  • 整数形式的字符串:^-?\d+$
  • 正整数形式的字符串:^[0-9]*[1-9][0-9]*$
  • 中国境内邮政编码:[1-9]\d{5}
  • 中文字符:[\u4e00-\u9fa5]
  • 国内电话号码:\d{3}-\d{8}|\d{4}-\d{7}
  • IP地址
    • \d{1, 3}. \d{1, 3}. \d{1, 3}. \d{1, 3}
    • 0-99:[1-9]?\d;100-199:1\d{2};200-249:2[0-4]\d;250-255:25[0-5]

re库

re库默认采用贪婪匹配方式,即输出匹配最长的字串。

re库官网

1
2
3
4
5
6
match = re.search(patten, string, flags=0) # 匹配一个字符串,不限定始末
match = re.match() # 匹配一个字符串,限定起始位置
lis = re.findall() # 匹配所有符合的字符串
iter_match = re.finditer() # 返回匹配结果的迭代类型,每个迭代元素为match对象
lis = re.split(maxsplit=0) # 按正则表达式匹配结果分割,将匹配结果去掉
string = re.sub(repl, count=0) # 替换所有匹配的字符串,返回替代后的字符串
  • patten:正则表达式,string:待匹配的字符串,以上五个函数都有这两个参数。
  • flags
    • re.I:忽略大小写
    • re.M:针对^操作,每行都可以作为匹配起始位置
    • re.S:针对.操作,可以匹配所有字符
  • maxsplit:最大分割数,剩余部分作为最后一个元素输出

  • repl:替换匹配字符串的字符串

  • count:最大替换次数
  • match对象
    • .group(0) 获得匹配后的字符串
    • .start() 匹配字符串在原始字符串的开始位置
    • .end() 匹配字符串在原始字符串的结束位置

实战

爬取淘宝商品及价格

  • 获取淘宝某商品搜索结果页面

    1
    2
    url = 'https://s.taobao.com/search?q=' + goodname # goodname为某商品名
    r = requests.get(url, headers)

    值得注意的是,在headers中需要设置cookie信息,可以手动登录淘宝后把cookie信息复制过来。

  • 解析HTML

    得到搜索结果HTML后,发现商品名字和价格分别在”raw_title”和”view_price”中,因此可以使用正则表达式来提取相关信息。

    1
    2
    nlt = re.findall(r'"raw_title":".+?"', page)  # 包含商品名信息列表
    plt = re.findall(r'"view_price":".+?"', page) # 包含价格信息列表

    去掉无用信息,得到商品名和价格列表

    1
    2
    for i in range(len(nlt)):
    ilt.append([eval(nlt[i].split(':')[1]), eval(plt[i].split(':')[1])]
  • 保存结果

    使用file.write()按照一定格式写入文件。

tips

  • 其实还可以观察到淘宝的翻页,只需要在url后加上&s=44,s的值加44实现一次翻页。
  • 整个过程使用requests库爬取HTML页面,再使用re库检索到有用信息,最后对有用信息加工存储。
  • requests每次只能爬取指定url的页面,因此需要事先观察要爬取网页的url信息。

爬取笔趣阁的小说

  • 获取指定书号的小说首页HTML(同上)

  • 解析HTML

    与爬取淘宝url基本保持不变的例子不同,一本小说有数百章节,每个章节都是一个独立的页面,因此需要事先在首页中获取到所有章节的url信息。

    • 通过观察得到所有章节的url都存放在属性class=listmain的div标签中的a标签中,因此我们使用BeautifulSoup库来检索到这些a标签。

      1
      2
      soup = BeautifulSoup(html,"html.parser")
      alist = soup.find("div",attrs={"class":"listmain"}).find_all("a")
    • 然后处理得到真正的url

      1
      chaptLinks.append(a.attrs["href"].split("/")[-1])
    • 接下来对每个章节页面进行分析,提取标题和正文。观察到标题在title标签中,正文在属性id=content,class=showtxt的div标签中。

      1
      2
      3
      title = soup.find("title").string.split("_")[0].strip()
      text = soup.find("div",attrs={"id":"content","class":"showtxt"}).text
      text = re.sub("\s+", "\n", text) # 用一个换行符替换1~多个空格
  • 保存结果(同上)

tips

  • 这个例子中使用了BeautifulSoup库来对标签信息检索,也用到了re库来对文本信息检索。