数据解析
1. 数据解析概述
1.1 数据解析方式
- re 解析
- bs4 解析
- xpath 解析
- pyquery 解析
1.2 数据解析原理
解析的局部的文本内容都会在标签之间或标签对应的属性中进行存储
- 进行指定标签的定位
- 标签或者标签对应的属性中存储的数据值进行提取(解析)
1.3 数据解析爬虫流程
-
指定 url
-
发起请求 get/post
-
获取相应数据
-
数据解析
-
持久化存储
2. re 解析
2.1 re 基础
2.1.1 正则表达式基础
正则表达式:对字符串操作的一种逻辑公式,一般用来对字符串进行匹配和筛选
常用元字符
代码 | 说明 |
---|---|
. | 匹配 除换行符以外的其他所有字符 |
\w | 匹配 数字,字母,下划线 |
\s | 匹配 任意的空白符 |
\d | 匹配 数字 |
\n | 匹配 换行符 |
\t | 匹配 制表符 |
\b | 匹配 单词的开始或结束 |
^ | 匹配 字符串的开始(在集合字符里[^a]表示非(不匹配)的意思 |
$ | 匹配 字符串的结束 |
常用反义字符
代码/语法 | 说明 |
---|---|
\W | 匹配 任意不是字母,数字,下划线,汉字的字符 |
\D | 匹配 任意非数字的字符 |
\S | 匹配 任意不是空白符的字符 |
\B | 匹配 不是单词开头或结束的位置 |
a|b | 匹配 a 或者 b |
( ) | 匹配()内的内容,也表示一个组 |
[…] | 匹配 字符组中的字符 |
[^…] | 匹配 除了字符组中的所有字符 |
常用限定词/量词
代码/语法 | 说明 |
---|---|
* | 重复 0 次或者更多次 |
+ | 重复 1 次或者更多次 |
? | 重复 0 次或者 1 次 |
{n} | 重复 n 次 |
{n, } | 重复 n 次或者更多次 |
{m, n} | 重复 n 次到 m 次 |
2.1.2 惰性匹配和贪婪匹配
- 贪婪匹配:正则表达式中包含重复的限定符时,通常的行为是匹配尽可能多的字符。
- ?,*,+,{}都属于贪恋匹配
- 懒惰匹配:有时候需要匹配尽可能少的字符
- 在上述量词后再加一个 ? 则属于惰性匹配 eg:*?,+?
2.1.3 匹配模式
- re.I: 忽略大小写
- re.M: 多行匹配
- re.S:单行匹配 让 . 可以匹配换行符
2.2re 模块常用函数
2.2.1 compile 方法
根据包含正则表达式的字符串创建模式对象,返回一个pattern对象
- re.compile (pattern = “flags”) flags 表示匹配模式
- re.compile (r"flags")
2.2.2 findall 函数
- re.findall (parttern,string)
- pattern.findall (string)
表示搜索字符串,然后以列表 形式返回全部匹配的子字符串
2.2.3 finditer 函数
- re.finditer (parttern,string)
- pattern.finditer (string)
表示搜索字符串,然后以迭代器 形式返回全部匹配的子字符串
2.2.4 match 方法和 search 方法
共同点:
- 两种方法都用于匹配子字符串
- 两种方法都返回 match 对象
不同点:
- match 仅从第一个字符开始匹配,匹配失败返回 None
- search 会逐个开始匹配,直到匹配成功或失败 即 search 可以匹配包含的第一次出现的子字符串
re.match (pattren , 子字符串,匹配模式)
re.search (pattern , 字符串,匹配模式)
2.2.5 group()
用于返回迭代器中的数据
2.3 re 爬虫实战
思路
- 拿到页面源代码
- 编写正则,提取页面数据
- 保存数据
2.3.1 需求 爬取 豆瓣Top 250数据
csv文件:文件中的数据以 “ , ” 隔开
使用时需要导入 csv模块
import re
import requests
import csv
url = "https://movie.douban.com/top250"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36"
}
fp = open("豆瓣top250.csv","w",encoding="utf-8")
obj = re.compile(r'<div class="item">.*?<em class="">(?P<num>.*?)</em>'
r'.*?<span class="title">(?P<title>.*?)</span>'
r'.*?<p class="">(?P<direct>.*?) '
r'.*?<br>(?P<years>.*?) '
r'.*?<span class="rating_num" property="v:average">(?P<score>.*?)</span>'
r'.*?<span>(?P<number>.*?)</span>'
r'.*?<span class="inq">(?P<comment>.*?)</span>', re.S)
for i in range(10):
start = i *25
param = {
"start":start
}
resp = requests.get(url=url,headers=headers,params=param)
page_source = resp.text
# print(page_source)
result = obj.finditer(page_source)
for item in result:
num = item.group("num")
title = item.group("title")
score = item.group("score")
direct = item.group("direct").strip()
years = item.group("years").strip()
number = item.group("number")
comment = item.group("comment")
fp.write(f"{num},{title},{score},{direct},{years},{number},{comment}\n")
fp.close()
resp.close()
print("爬取完成")
2.3.2 需求 爬取 电影天堂电影信息
import re
import requests
url = "https://www.dy2018.com/"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36"
}
obj_1 = re.compile(r"2022必看热片.*?</ul>",re.S)
obj_2 = re.compile(r"<li><a href='(?P<add>.*?)' title=(?P<name>.*?)>",re.S)
obj_3 = re.compile(r'<td style="WORD-WRAP: break-word" bgcolor="#fdfddf"><a href="(?P<link_1>.*?)">',re.S)
resp = requests.get(url=url,headers=headers)
resp.encoding = "gb2312"
page_source = resp.text
# 抓取必看热片部分的页面代码
result_1 = re.findall(obj_1,page_source)
result_2 = re.finditer(obj_2,result_1[0])
for item in result_2:
url_child =url+item.group("add")[1:]
print(item.group("name")[1:-1])
resp_2 = requests.get(url=url_child,headers=headers)
resp_2.encoding = "gb2312"
page_source_1 = resp_2.text
result_3 = obj_3.finditer(page_source_1)
for item_1 in result_3:
print(item_1.group("link_1"))
print("")
resp.close()