爬虫——爬取百度贴吧每个帖子里面的图片
现在我们用正则来做一个简单的爬虫,我们尝试爬取某个百度贴吧里面的所有帖子,并且将这个帖子里首页每个楼层发布的图片下载到本地。
分析:以美女吧为例
第一页:https://tieba.baidu.com/f?kw=%E7%BE%8E%E5%A5%B3&pn=0
第二页:https://tieba.baidu.com/f?kw=%E7%BE%8E%E5%A5%B3&pn=50
第三页:https://tieba.baidu.com/f?kw=%E7%BE%8E%E5%A5%B3&pn=100
……
可以发现,url地址中pn及其前面的部分是相同的,改变的只是pn后面的值
不难发现,每页中共有50个帖子,所有pn的值是以每页50的值递增。
则第page页的pn值为:(page - 1) * 50
而kw=%E7%BE%8E%E5%A5%B3为urllib.parse.urlencode{"kw":"美女"}
#!/usr/bin/python3
# -*- coding:utf-8 -*-
__author__ = 'mayi'
"""
用正则做一个简单的爬虫:尝试爬取某个贴吧里的所有帖子,并且将帖子里每层楼发布的图片下载到本地。
例:美女吧(https://tieba.baidu.com/f? + kw=%E7%BE%8E%E5%A5%B3 + &pn=50)
https://tieba.baidu.com/f?kw=%E7%BE%8E%E5%A5%B3&pn=50
其中:
kw=%E7%BE%8E%E5%A5%B3为urllib.parse.urlencode({"kw":"美女"})
pn为值从0开始,每页为50个帖子,故:
第一页,pn的值为:0
第二页,pn的值为:50
第三页,pn的值为:100
……
"""
import os
import re
import urllib.request
# 一个爬虫类
class Spider(object):
"""
一个爬虫类
"""
# 初始化
def __init__(self, name):
"""
类的初始化
:param name: 百度贴吧名
:return:
"""
# User-Agent头
self.header = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36'}
keyword = urllib.parse.urlencode({"kw":name})
self.url = "https://tieba.baidu.com/f?" + keyword + "&pn="
self.run()
# 获取帖子的最后一页
def getLastPage(self, url):
"""
获取url帖子的尾页的pn值
:param url: url地址
:return: pn值
"""
html = self.loadPage(url)
html = html.decode("utf-8")
# 利用正则匹配尾页的pn值
pattern = re.compile(r'<a href=.*?pn=(\d+).*?>尾页</a>')
try:
pn = pattern.findall(html)[0]
pn = int(pn)
except:
pn = 1
return pn
# 爬虫开始工作
def run(self):
"""
爬虫开始工作
:return:
"""
start_page = 1
end_page = self.getLastPage(self.url) // 50 + 1
print("该吧共" + str(end_page) + "页")
for page in range(start_page, end_page + 1):
# 计算pn的值
pn = (page - 1) * 50
# 拼接成完整的url地址
full_url = self.url + str(pn)
# 调用loadPage()函数,下载full_url的页面内容
html = self.loadPage(full_url)
# 调用screenPage()函数,筛选下载的页面内容
item_list = self.screenPage(html)
print("正在下载第" + str(page) + "页,共" + str(len(item_list)) + "个帖子")
# 调用loadImage()函数,下载帖子里首页的图片
self.loadImage(item_list)
# 下载网页内容
def loadPage(self, url):
"""
下载url的页面内容
:param url: 需下载页码的url地址
:return: 页面内容
"""
# url 连同 headers,一起构造Request请求,这个请求将附带 chrome 浏览器的User-Agent
request = urllib.request.Request(url, headers = self.header)
# 向服务器发送这个请求
response = urllib.request.urlopen(request)
# 获取网页内容:bytes
html = response.read()
return html
# 筛选内容
def screenPage(self, html):
"""
筛选内容:筛选出每层楼帖子的链接
:param html: 页面内容
:return: 需下载的图片链接地址
"""
# 转码:bytes转utf-8
html = html.decode("utf-8")
# 利用正则匹配每层楼帖子的链接
pattern = re.compile(r'<a href="(/p/\d+).*?>.*?</a>', re.S)
item_list = pattern.findall(html)
return item_list
# 下载帖子里首页里面的图片
def loadImage(self, item_list):
"""
下载帖子里首页里面的图片
:param item_list: 需下载图片的帖子的ID列表
:return: None
"""
for id in item_list:
# 根据id拼接帖子链接
link = "https://tieba.baidu.com" + id
start_page = 1
end_page = self.getLastPage(link)
print("正在下载帖子:" + link + " 中图片")
image_no = 0
for page in range(start_page, end_page + 1):
# 拼接完整链接
full_link = link + "?pn=" + str(page)
# 获取需下载图片的帖子的页面内容
html = self.loadPage(full_link)
# 转码:bytes转utf-8
html = html.decode("utf-8")
# 利用正则匹配帖子中的图片链接
pattern = re.compile(r'<img class="BDE_Image".*?src="(.*?)".*?>', re.S)
# 图片链接列表
image_list = pattern.findall(html)
for image in image_list:
image_no = image_no + 1
self.writeImage(image, id[3:], image_no)
print("帖子:" + full_link + " 共下载" + str(len(image_list)) + "个图片")
# 向本地磁盘中存储图片
def writeImage(self, url, id, image_no):
"""
向本地磁盘中存储图片
:param url: 图片url
:param id: 帖子id:本地文件夹
:param image_no: 图片序号
:return:
"""
# 判断是否存在对应的文件夹
if not os.path.exists("image/" + id):
# 若不存在,则创建
os.makedirs("image/" + id)
# 图片文件名:id + "_" + 5位图片序号
image_no = str(image_no)
file_name = "image/" + id + "/" + id + "_" + "0" * (6 - len(image_no)) + image_no + ".jpg"
# 以wb方式打开文件
file = open(file_name, "wb")
# 获取图片名为名为
images = self.loadPage(url)
# 写入图片
file.write(images)
# 关闭文件
file.close()
# 主函数
if __name__ == '__main__':
# 要爬取的百度贴吧名
name = input("请输入您要爬取的百度贴吧名:")
# 创建一个爬虫对象
mySpider = Spider(name)