本篇博客主要讲解urllib3和re的简单应用,同时简单介绍正则表达式的使用,做一个抓取猫眼电影信息的小程序。
准备
首先我们先相关库导入
import urllib3
import re
import json
import time
import csv
打开网页:https://maoyan.com/board/4
一、请求库(urllib3)
1、导入库:import urllib3
2、定义请求对象:http = urlli3.PoolManager()
3、进行get请求: resp = http.request('GET',url,headers=ua)
4、查看状态码:resp.status
5、查看网页源代码:resp.data #以二进制形式显示汉字
6、编码转换:resp.data.decode('utf-8')
首先我们要有一个程序入口,也就是mian main:
import urllib3
import re
import json
import time
# 请求网页
def page_def(url, ua):
http = urllib3.PoolManager()
resp = http.request('GET', url, headers=ua)
return resp.data
#程序主入口
if __name__ == '__main__':
ua = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36'}
url = 'https://maoyan.com/board/4'
html_info = page_def(url, ua)
这里的ua是客户端信息 (User-Agent),不加容易抓取失败。
你可以选用自己的ua,也可以百度从上面拿几个。
url就是你要抓取的网页地址。
page_def 函数用来请求网页,讲请求结果放在html_info中。
二、解析网页(正则表达式的使用)
正则表达式使用步骤
1)寻找规律
2)用正则符号表示规律
3)提取信息
1> 点号’.
’
作用:可以代替除换行符以外的任何一个字符
2> 星号 ‘*
’
作用:一个星号可以表示它前面的一个子表达式0次或无数次。
3> 问号’?
’
作用:一个问号可以表示前面的子表达式0次或1次
4> .*
贪婪模式的匹配
特点:尽可能多的匹配字符
.*?
非贪婪模式的匹配
特点:尽可能少的匹配字符
5>re.findall(正则,字符串,参数)
参数:re.S (忽略换行符)
6)括号
作用:正则表达式中,用括号括住的就是我们要取的
我们分析一下网页源码:
先看第一个序号,找到它的特征:
它是在< dd >标签中的。而这个标签是电影信息所特有的。
在分析<i class="board-index board-index-1">1</i>
我们取特征:board-index
< dd>
和board-index
之间隔了一些字符,用.*?
非贪婪模式匹配.
我们可以看到序号左右两边都有尖括号,*?>(.*?)<
获取序号信息
结合起来就是:pat = r'<dd>.*?board-index.*?>(.*?)<'
这个就是获取序号的正则。
依此类推:
pat = r'<dd>.*?board-index.*?>(.*?)<' \
+ r'.*?data-src="(.*?)"' \
+ r'.*?name.*?a.*?>(.*?)<' \
+ r'.*?star.*?:(.*?)<' \
+ r'.*?releasetime.*?:(.*?)<' \
+ r'.*?integer.*?>(.*?)<' \
+ r'.*?fraction.*?>(.*?)<'
分别获取序号、图片地址、电影名、主演、上映时间、评分。
(最后一个评分分为两部分,整数部位和小数部位)
然后就可以写解析网页的函数:
# 解析网页元素
def info_def(html_info):
html = html_info.decode('utf-8')
pat = r'<dd>.*?board-index.*?>(.*?)<' \
+ r'.*?data-src="(.*?)"' \
+ r'.*?name.*?a.*?>(.*?)<' \
+ r'.*?star.*?:(.*?)<' \
+ r'.*?releasetime.*?:(.*?)<' \
+ r'.*?integer.*?>(.*?)<' \
+ r'.*?fraction.*?>(.*?)<'
m_index = re.findall(pat, html, re.S)
return m_index
将解析结果存入m_index
三、写入本地
1、将数据存入字典,方便操作
def dic_def(m_index):
l1 = []
for info_one in m_index:
info_dirc = {'序号': info_one[0],
'图片网址': info_one[1],
'电影名': info_one[2],
'主演': info_one[3],
'放映时间': info_one[4],
'评分': info_one[5] + info_one[6]
}
l1.append(info_dirc)
return l1
2、分别保存为txt、csv、png格式
# 保存文本
def write_def(l1):
with open(r'D:\test\movie_info.txt', 'a', encoding='utf-8') as af:
for i in l1:
af.write(json.dumps(i, ensure_ascii=False) + '\n')
# 保存图片
def img_def(m_index):
for i in m_index:
img_resp = http.request('GET', i[1])
with open(r'D:\test\png\%s.jpg' % (i[2]), 'bw') as imgf:
imgf.write(img_resp.data)
#保存表格
def csv_def(l1):
with open(r'D:\test\movie_info.csv', 'w', encoding='gbk', newline='') as cf:
writer = csv.DictWriter(cf,fieldnames = ['序号','图片网址','电影名','主演','放映时间','评分'])
writer.writeheader()
writer.writerows(l1)
四、完整程序代码
import urllib3
import re
import json
import time
import csv
# 请求网页
def page_def(url, ua):
http = urllib3.PoolManager()
resp = http.request('GET', url, headers=ua)
return resp.data
# 解析网页元素
def info_def(html_info):
html = html_info.decode('utf-8')
pat = r'<dd>.*?board-index.*?>(.*?)<' \
+ r'.*?data-src="(.*?)"' \
+ r'.*?name.*?a.*?>(.*?)<' \
+ r'.*?star.*?:(.*?)<' \
+ r'.*?releasetime.*?:(.*?)<' \
+ r'.*?integer.*?>(.*?)<' \
+ r'.*?fraction.*?>(.*?)<'
m_index = re.findall(pat, html, re.S)
return m_index
# 把数据写入字典
def dic_def(m_index):
l1 = []
for info_one in m_index:
info_dirc = {'序号': info_one[0],
'图片网址': info_one[1],
'电影名': info_one[2],
'主演': info_one[3],
'放映时间': info_one[4],
'评分': info_one[5] + info_one[6]
}
l1.append(info_dirc)
return l1
# 写到本地
def write_def(l1):
with open(r'D:\test\movie_info.txt', 'a', encoding='utf-8') as af:
for i in l1:
af.write(json.dumps(i, ensure_ascii=False) + '\n')
# 保存图片
def img_def(m_index):
http = urllib3.PoolManager()
for i in m_index:
img_resp = http.request('GET', i[1])
with open(r'D:\test\png\%s.jpg' % (i[2]), 'bw') as imgf:
imgf.write(img_resp.data)
# 保存表格
def csv_def(l1, i):
with open(r'D:\test\movie_info.csv', 'a', encoding='gbk', newline='') as cf:
writer = csv.DictWriter(cf, fieldnames=['序号', '图片网址', '电影名', '主演', '放映时间', '评分'])
if i == 0:
writer.writeheader() # 避免重复写头
writer.writerows(l1)
# 进度条
def progress(n, a):
m = int(n / a * 100)
x = int(n / a * 10)
print("\r[%s%s] %s%%" % ("█" * x, " " * (10 - x), m), end="")
if __name__ == '__main__':
progress(0, a=10)
ua = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36'}
for j in range(0, 10, 1):
url = 'https://maoyan.com/board/4?offset=%d' % (10 * j)
html_info = page_def(url, ua)
m_index = info_def(html_info)
l1 = dic_def(m_index)
write_def(l1)
img_def(m_index)
csv_def(l1, j)
time.sleep(2) # 让他睡一会,不要频繁抓取,不然容易被反
progress(j + 1, a=10)
结果展示: