前言
楼主16年毕业,工作内容目前主要以ETL开发为主。有一段时间逛知乎,经常看到有人分享一些数据分析的文章,比如美团上的点餐分析、豆瓣的评分分析,还有网易云音乐热评分析之类的。感觉这些文章都是有实打实的东西在,而且作者们的文笔也深得我心。后来也知道,这些数据大部分是用python爬虫爬取的,所以我也开始断断续续在看相关的内容。本帖就是我在做其中一部分实战的心路历程了,权当记录一下,水平较低莫怪。
1.起因
虎扑是广大jrs的家园,步行街是这个家园里最繁华的地段。据称广大jrs平均学历985,步行街街薪30w起步。
大学时经舍友安利,开始了解虎扑,主要是看看NBA的一些资讯。
偶尔也上上这个破街,看看jrs虐虐狗,说说家长里短等等,别的不说,jr们的三观都是特别正的。
《不冷笑话》基本是我每天必看的帖子,感觉《不冷笑话》的几位老伙计非常敬业,每天都会有高质量的输出,帖子下的热帖也很给力,福利满满。
正学python,突发奇想想把《不冷笑话》的图都爬下来。
引用自我的博客园文章:
爬取虎扑NBA首页主干道推荐贴的一只小爬虫,日常爬不冷笑话解闷
另外随着我学习内容的增加,一个爬虫也由最简单的流水线式的结构,变为模块化的设计思路,之后又变为使用成熟的框架。下面就切入正题了。
2.经过
版本1:模块化的简单实现
其实python的上手非常简单,原因有两个,python语言就很简洁,python的包非常丰富。我在B站上跟着小甲鱼的视频没学个十几节课,就开始对照着别人最简单的爬虫例子在做了。
新手无非就是requests + BeautifulSoup,一个负责HTTP请求,一个负责HTML解析。就小规模爬虫来讲,主要时间还是用来定位爬取目标在页面中的位置。所以,先看一下最low的爬虫是什么样子。
# -*- coding: utf-8 -*-
import requests
from bs4 import BeautifulSoup
import os, time
import re
link = r'https://www.qiushibaike.com/pic/'
r = requests.get(link)
x = 1
soup = BeautifulSoup(r.text,'lxml')
for content in soup.find_all(class_ = 'thumb'):
xpath = content.find('img').get('src')
image = requests.get('http:' + xpath)
open('qiubai %d.jpg' % x,'wb').write(image.content)
x += 1
除去导入包和编码声明,十行代码就能写一个爬虫,用来爬取糗事百科网站上的图片,并保存成本地文件。
实际上,一开始要准备爬取《不冷笑话》时,帖子地址的获取是一个非常困扰我的问题。后来经过我长期观察发现,《不冷笑话》总是在 虎扑NBA 的固定位置出现,所以我只要在这个页面定位好就可以进去了。
一开始我也是用最简单的结构去做《不冷笑话》的爬虫,但是又觉得这样做好像是重用很低,所以,按照模块化的思路,划定了几个功能点:
- 1、定位不冷笑话在首页的位置,获取链接和标题
- 2、建立以标题命名的目录,如果目录存在,说明已下载,程序结束
- 3、进入不冷笑话的界面,获取正文中的图片链接,存入列表
- 4、获取亮贴中的图片链接,存入列表
- 5、保存图片,根据传入参数为正文或评论进行命名,区分图片来源
- 6、大功告成
最终基本都按照了功能划分了模块,写出来的是这个样子的。
#-*- coding: utf-8 -*-
import requests
from bs4 import BeautifulSoup
import os, time
import re
url = (r'https://nba.hupu.com/')
#获取不冷笑话在首页的位置,返回url和标题
def get_buleng_title_url(url):
index_html = requests.get(url)
index_html_s = BeautifulSoup(index_html.text,'lxml')
main_street = index_html_s.find(class_ = 'gray-list main-stem max250')
url_list = []
url_name_list = []
for dd in main_street.find_all('dd',limit = 5):
url_list.append(dd.a.get('href'))
url_name_list.append(dd.a.get_text())
return [url_list[4],url_name_list[4]]
#获取不冷笑话正文中的图片列表,利用set去重
def get_pic_url(buleng_list):
pic_url_list = set()
buleng_html = requests.get(buleng_list[0])
buleng_html_s = BeautifulSoup(buleng_html.text,'lxml')
buleng_content = buleng_html_s.find(class_='quote-content')
for pic_url in buleng_content.find_all('img'):
try:
original_url = pic_url.get('data-original')
pic_url_list.add(original_url.split('?')[0])
except:
pic_url_list.add(pic_url.get('src'))
return pic_url_list
#创建以标题命名的文件夹,并返回是否创建成功
def makedir(buleng_list):
path = ('E:\\pic\\buleng\\%s' % buleng_list[1])
if os.path.exists(path):
return 0
else:
os.makedirs(path)
return path
#获取亮贴中的图片列表,set去重
def get_comment_pic_url(buleng_list):
comment_pic_url_list = set()
buleng_html = requests.get(buleng_list[0])
buleng_html_s = BeautifulSoup(buleng_html.text,'lxml')
buleng_comment = buleng_html_s.find(id='readfloor')
for floor in buleng_comment.find_all('table'):
for pic_url in floor.find_all('img'):
try:
original_url = pic_url.get('data-original')
comment_pic_url_list.add(original_url.split('?')[0])
except:
comment_pic_url_list.add(pic_url.get('src'))
return comment_pic_url_list
#下载图片,可下载gif、jpg、png格式
def download_pic(pic_url_list,path,pic_from = '正文'):
a = 1
for url in pic_url_list :
if url.endswith('.gif'):
pic = requests.get(url)
with open((path+('\\%s-%s.gif' % (pic_from,a))),'wb') as f:
f.write(pic.content)
f.close
print('下载一张%s动图' % pic_from)
a += 1
if url.endswith('.jpg'):
pic = requests.get(url)
with open((path+('\\%s-%s.jpg' % (pic_from,a))),'wb') as f:
f.write(pic.content)
f.close
print('下载一张%sjpg图' % pic_from)
a +=1
if url.endswith('.png'):
pic = requests.get(url)
with open((path+('\\%s-%s.png' % (pic_from,a))),'wb') as f:
f.write(pic.content)
f.close
print('下载一张%spng图' % pic_from)
a +=1
if __name__ == "__main__":
buleng = get_buleng_title_url(url)
path = makedir(buleng)
if path != 0:
pic_url_list = get_pic_url(buleng)
comment_pic_url_list = get_comment_pic_url(buleng)
download_pic(pic_url_list,path)
download_pic(comment_pic_url_list,path,'评论')
else:
print('目录已存在,等待虎扑更新')
版本2:模块化的简单实现plus
后来我一想,反正都是获取了主干道所有的推荐贴,为啥不自定义要下载的帖子呢,这样的话,这十几个推荐贴想下哪个下哪个,不是功能更上一层楼了?
多亏了模块化的设计,博主略一沉吟,部分修改如下:
#获取热帖在首页的位置,返回url和标题
def get_retie_title_url(url):
index_html = requests.get(url)
index_html_s = BeautifulSoup(index_html.text,'lxml')
main_street = index_html_s.find(class_ = 'gray-list main-stem max250')
url_list = []
url_name_list = []
for dd in main_street.find_all('dd',limit = 14):
url_list.append(dd.a.get('href'))
url_name_list.append(dd.a.get_text())
return [url_list,url_name_list]
#return [url_list[4],url_name_list[4]] 源代码对比
#获取热帖正文中的图片列表,利用set去重
def get_pic_url(retie_list,num): #新加num参数
pic_url_list = set()
retie_html = requests.get(retie_list[0][num-1])
retie_html_s = BeautifulSoup(retie_html.text,'lxml')
retie_content = retie_html_s.find(class_='quote-content')
for pic_url in retie_content.find_all('img'):
try:
original_url = pic_url.get('data-original')
pic_url_list.add(original_url.split('?')[0])
except:
pic_url_list.add(pic_url.get('src'))
return pic_url_list
if __name__ == "__main__":
retie = get_retie_title_url(url)
a = 1
for retie_title in retie[1]:
print(a,' ',retie_title)
print('--------------------------------')
a += 1
num = int(input('请输入要下载的热帖排序(1-14)')) #获取输入序号
if num < 1 or num > 14:
print('不合法')
else:
path = makedir(retie[1][num-1])
if path != 0:
pic_url_list = get_pic_url(retie,num)
comment_pic_url_list = get_comment_pic_url(retie,num)
download_pic(pic_url_list,path)
download_pic(comment_pic_url_list,path,'评论')
else:
print('目录已存在,等待虎扑更新')
然后效果就是这样啦
3.总结
好了,今天的无干货流水账就先告一段落,欲知后事如何,且听下回分解