0x000 前言
最近和公司的IOS谈起,以前我们是做移动端,现在已经和前端混为一谈了。 也是 , 做了一年多的Android ,一直都是在写界面展示数据,写交互 ,存储简单的数据 ,最主要的业务数据与业务逻辑都在服务器端,这让我有种离了服务端,我们只能玩玩单机和联机了 。作为曾经的web开发,深知服务端的重要性,我们要想做一款好的产品,没有服务端是万万不能的 , 而我们做移动端的更是要知晓一些服务端的知识 。
我有一个梦想,做一款属于自己的产品 ,但苦于没有足够的数据,于是就开始了我的Python爬虫之旅 。(如果有时间,我会写如何从零收集数据-->服务器端的建立 --> 客户端的编写 , 一整套教程)
0x001 Python基础
Python是一门很简洁的语言 , 因为市面上介绍Python语法的教程很多 , 大多数都很好 ,我这里就不啰嗦了 , 下面贴出我看过的网站:
0x002 网络爬虫
网络爬虫是一个自动提取网页的程序,它为搜索引擎从万维网上下载网页,是搜索引擎的重要组成。传统爬虫从一个或若干初始网页的URL开始,获得初始网页上的URL,在抓取网页的过程中,不断从当前页面上抽取新的URL放入队列,直到满足系统的一定停止条件。[来自百度]
我们通过URL让爬虫来爬取我们想要的内容 , 根据我们的规则进行深入爬取 , 比如说 , 我们想要爬取正在上映的电影数据 , 并下载电影海报,其他的内容我们忽略掉,那么我们就需要根据网页内容的规则来制订,哪些内容是我们需要的,哪些是我们不需要的。
0x003 爬取豆瓣电影数据
第一步 : 打开豆瓣电影 , 分析网页结构
我们爬取的网址是 https://movie.douban.com/cinema/nowplaying/shenzhen/
,里面有一个的模块叫正在上映 , 我们就解析这个模块,其他的内容我们忽略掉。chrome(firefox)按f12查看网页结构。
我们可以用元素选择工具,选中我们要分析的数据,firebug控制台就会将选中html结构展示出来。我们通过分析 , 我们想要的数据是在一个ul标签里面,每一个li标签就是一部电影的数据,我们只需要取出里面的li中的数据即可。豆瓣的前台使用了数据绑定的技术 ,这为我们获取数据,方便了不少 ,我们直接取出li中的属性就可以将电影信息获取到了 。
第二步 : 下载网页内容
我们需要将网页中的数据提取出去 , 就需要先将网页内容下载下来,写Java程序的朋友应该知道 , 如果使用Java内置网络请求,将网页数据下载下来,那将要写多少行代码,实话说,Java的网络请求真的很不友好,幸而Java社区异常强大,涌现了一批又一批比较好用的网络请求框架,如OkHttp,liteHttp等等,大大简化了我们的工作。但是,在Python中,将一个网页数据下载下来,只需要一行 。
with request.urlopen('https://movie.douban.com/cinema/nowplaying/shenzhen/') as response:
这是Python内置的urllib下的requests对象,将下载好的二进制数据封装在response对象里面,使用readlines
方法就可以将其读取出来 。
from urllib import request
from urllib import parse
# 打开链接,并得到返回值
with request.urlopen('https://movie.douban.com/cinema/nowplaying/shenzhen/') as response:
conent_list = response.readlines() # 得到一个byte类型的list
#打印内容
for content in conent_list:
print(content .decode()) # 因为content是byte类型,所以需要解码成str类型
虽然内置urlib可以完成我们的需求,但是我们不会用系统内置,因为还是比较麻烦,我们将使用富有盛名的requets库,虽然名字和系统内置的差不多,但是确实截然不同的库 。系统库是在urllib模块下,而requests就在requests模块下 。
安装requests
pip install requests
example
# url 请求地址
# headers 请求头
url = 'https://movie.douban.com/cinema/nowplaying/shenzhen/'
res = requests.get(url,headers=headers)
res.text() # 得到请求的文本内容
好戏,马上开场
第三步 : 解析网页内容
解析是使用的Python内置的Html解析器,类似Java的jsoup.jar提供的api 。都是通过遍历Html dom树来进行分析,判断需要的tag ,然后进行属性解析。Python正在强大的Html解析器是,XPath解析,也是scrapy爬虫库内置的解析器 ,当然还是有beautifulsoup 。
1.引入HTMLPaser
from html.parser import HTMLParser
2.新建解析类,继承HTMLPaser
class MoviesParser(HTMLParser):
3.overload handle_starttag方法 , 解析标签
class MoviesParser(HTMLParser):
def __init__(self):
HTMLParser.__init__(self)
# 电影列表list集合
self.movies = []
# 标签是否在li标签中的标识
self.img_into_movie = False
self.a_into_movie = False
def handle_starttag(self, tag, attrs):
# 因为属性都是key=val形式 , 所以根据属性名称取值
def _attr(attrs,attr_name):
for attr in attrs:
if attr[0] == attr_name:
return attr[1]
return None
# 取出属性值 , 根据每个Item的特征取值 ,因为有些Item的属性可能会重复 , 所以要尽量找出差异性,这样才能保证数据的准确性
if tag == 'li' and _attr(attrs,'data-title') and _attr(attrs,'data-category') == 'nowplaying':
movie = {}
movie['title'] = _attr(attrs,'data-title')
movie['score'] = _attr(attrs,'data-score')
movie['star'] = _attr(attrs,'data-star')
movie['duration'] = _attr(attrs,'data-duration')
movie['region'] = _attr(attrs,'data-region')
movie['director'] = _attr(attrs,'data-director')
movie['actors'] = _attr(attrs,'data-actors')
self.movies.append(movie)
self.img_into_movie = True
self.a_into_movie = True
#获取海报图片
if tag == 'img' and self.img_into_movie:
self.img_into_movie = False
img_src = _attr(attrs,'src')
movie = self.movies[len(self.movies) -1]
movie['poster_url'] = img_src
# 下载图片
donwload_poster_url(img_src)
# 解析a标签,提取电影详情页的Url
if tag == 'a' and self.a_into_movie:
if _attr(attrs,'data-psource') == 'title':
self.a_into_movie = False
movie_url = _attr(attrs,'href')
movie = self.movies[len(self.movies) -1]
movie['movie_url'] = movie_url
这是爬虫中最关键的部分,数据解析是保证数据正确的性的地方,解析没做好,就可能存在很多脏数据,这是我们应当避免的 。
第四步 : 下载图片
# 下载图片
def donwload_poster_url(url):
res = requests.get(url)
file_name = str.split(url,'/')[-1]
file_path = 'poster_img/' + file_name
print('download img file_path = ',file_path)
with open(file_path,'wb') as f:
f.write(res.content)
我们直接使用requests库,get图片地址,得到图片的二进制数据,再见二进制数据写入到文件中,这样我们的图片文件就下载好了。
完整源码
# 使用requests爬豆瓣正在上映的电影
from html.parser import HTMLParser
import requests
class MoviesParser(HTMLParser):
def __init__(self):
HTMLParser.__init__(self)
self.movies = []
self.img_into_movie = False
self.a_into_movie = False
def handle_starttag(self, tag, attrs):
# 根据属性名称取值
def _attr(attrs,attr_name):
for attr in attrs:
if attr[0] == attr_name:
return attr[1]
return None
# 取出属性值
if tag == 'li' and _attr(attrs,'data-title') and _attr(attrs,'data-category') == 'nowplaying':
movie = {}
movie['title'] = _attr(attrs,'data-title')
movie['score'] = _attr(attrs,'data-score')
movie['star'] = _attr(attrs,'data-star')
movie['duration'] = _attr(attrs,'data-duration')
movie['region'] = _attr(attrs,'data-region')
movie['director'] = _attr(attrs,'data-director')
movie['actors'] = _attr(attrs,'data-actors')
self.movies.append(movie)
self.img_into_movie = True
self.a_into_movie = True
#获取海报图片
if tag == 'img' and self.img_into_movie:
self.img_into_movie = False
img_src = _attr(attrs,'src')
movie = self.movies[len(self.movies) -1]
movie['poster_url'] = img_src
donwload_poster_url(img_src)
if tag == 'a' and self.a_into_movie:
if _attr(attrs,'data-psource') == 'title':
self.a_into_movie = False
movie_url = _attr(attrs,'href')
movie = self.movies[len(self.movies) -1]
movie['movie_url'] = movie_url
# 下载图片
def donwload_poster_url(url):
res = requests.get(url)
file_name = str.split(url,'/')[-1]
file_path = 'poster_img/' + file_name
print('download img file_path = ',file_path)
with open(file_path,'wb') as f:
f.write(res.content)
def douban_movies(url):
#首先构建请求头 ,模拟浏览器请求头
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'}
# # 打开链接,并得到返回值
res = requests.get(url,headers=headers)
# 创建Html解析器
moviesParser = MoviesParser()
# 解析Html数据
moviesParser.feed(res.text)
return moviesParser.movies
if __name__ == '__main__':
url_str = 'https://movie.douban.com/cinema/nowplaying/shenzhen/'
movies_res = douban_movies(url_str)
import json
json_str = json.dumps(movies_res,sort_keys=True,indent=4,separators=(',',': '))
# 打印json数据
print(json_str)
从下载数据到解析Html,只用了七十多行 ,包含了注释和空格,真是人生苦短,我用Python 。
0x004 结语
Python确实是一个比较简洁的语言,学起来也相对比较轻松,各种库应有尽有,可以作为获取数据的不二选择 。其实,解析Html 还可以简化,使用Xpath更加简洁,几行代码就可以搞定,在后续的文章中,我会逐一介绍 。
有时候感觉文字的表现力真是太弱了,特别是对于技术文章,因为涉及的多,很多时候写着写着就啰嗦了,然后删掉 。 俗话说:子不如表 , 表不如图,图不如视频 。最近关注了一个简书的作者,他做了一系列的视频,讲得挺好的,我思考着,要不要出个爬虫的视频教程 。