hello hello,小伙伴们你们好,今天我就正式进入爬虫稍微高级一点的阶段了,哈哈哈哈上一篇才是入门了,本章就进阶了,所以没学好入门的小伙伴们先去我的上一篇文章看懂看明白啊,丢个小链接:
温馨提示:
好的,接下来我们继续进阶之路 爬虫第二式
大家看到了,本篇的标题是爬取猫眼电影前100排行榜,在这之前我想了想,我觉得有必要温习一下上一篇的技术,所以,我们先来做个案例,把上一篇的技术点都组合起来用一下,再开始我们相当相当相当正规的猫眼电影前100排行榜,如果不想看就直接翻到下面,直接看本章的正式内容吧~~
需求:
1、输入贴吧名称: 张一山吧
2、输入起始页: 1
3、输入终止页: 2
4、保存到本地文件:张一山吧_第1页.html、张一山吧_第2页.html
实现总步骤:
【1】查看所抓数据在响应内容中是否存在
右键 - 查看网页源码 - 搜索关键字
【2】查找并分析URL地址规律
第1页: http://tieba.baidu.com/f?kw=???&pn=0
第2页: http://tieba.baidu.com/f?kw=???&pn=50
第n页: pn=(n-1)*50
【3】发请求获取响应内容
【4】保存到本地文件
第一步:
我们看途中有个这样的字样:哪位大神帮忙找找呀
我们直接进鼠标右键单击查看查看网页源码
我们 Ctrl+f 搜索字样,看到是有的,那就可以,为什么这样做呢?
额。。。。。好像上一篇没有说。。。。。在这里说下吧,因为有些网站的内容是动态加载出来的,不是直接响应出来的,这也是一种反爬机制,后面我们会讲怎么突破这样的,但是记住了,以后在爬去页面的时候
先检查源代码中是否有关键字这样的内容,如果有就是普通网站,没有就是反爬比较好的,这一点很重要,千万千万千万记住了!!!
第二步
接着,也就是我们上面看到的,获取到了规律就像这样,对把:
第1页: http://tieba.baidu.com/f?kw=???&pn=0
第2页: http://tieba.baidu.com/f?kw=???&pn=50
第n页: pn=(n-1)*50
第三步
html = requests.get(url=url, headers=self.headers).content.decode('utf-8')
就是我们之前说过的,requests请求、和字符串拼接、还要掩藏请求头,不能让网站知道我们是爬虫等等吧,就下来我们分析完了就开始写代码吧
# 导入各种库
import requests #导入requests库,发送请求用
from urllib import parse # urllib,这个是给URL地址中查询参数进行编码
import time # 时间模块
import random # 随机数模块
time、random模块的导入,我们下面再说
我们接着,用函数的方法,定义各个模块吧,不会函数的小朋友我没办法了,我没写过Python函数的文章,可以去看看别的
class TiebaSpider:
def __init__(self):
"""定义常用变量"""
self.url = 'http://tieba.baidu.com/f?kw={}&pn={}'
self.headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1'}
我们定义一个类,实例化常用变量,因为经常要用到嘛,所有定义类和函数就比较方便
就是这样,把要爬取的URL地址放上去,变量要用大括号代替,下面我们会给到参数,然后要掩盖你的请求头,作用就不多说了
接着我们定义请求功能函数
def get_html(self, url):
"""请求功能函数"""
html = requests.get(url=url, headers=self.headers).content.decode('utf-8')
return html
用到上面我们实例化的URL地址和请求头了,直接return出去
接着我们定义数据处理函数
def save_html(self, filename, html):
"""数据处理函数"""
with open(filename, 'w') as f:
f.write(html)
获取到的内容我们保存下来,直接写入到文件里面,保存到本地
在接下来就是程序入口函数
def run(self):
"""程序入口函数"""
name = input('请输入贴吧名:')
start = int(input('请输入起始页:'))
end = int(input('请输入终止页:'))
# 编码
params = parse.quote(name)
# 拼接多页的URL地址
for page in range(start, end + 1):
pn = (page - 1) * 50
page_url = self.url.format(params, pn)
# 请求 + 解析 + 数据处理
html = self.get_html(url=page_url)
filename = '{}_第{}页.html'.format(name, page)
self.save_html(filename, html)
# 终端提示
print('第%d页抓取完成' % page)
# 控制数据抓取的频率
time.sleep(random.randint(1, 2))
最后直接main一下:
if __name__ == '__main__':
spider = TiebaSpider()
spider.run()
最后奉上完整代码:
"""
1、输入贴吧名称: 赵丽颖吧
2、输入起始页: 1
3、输入终止页: 2
4、保存到本地文件:赵丽颖吧_第1页.html、赵丽颖吧_第2页.html
"""
import requests
from urllib import parse
import time
import random
class TiebaSpider:
def __init__(self):
"""定义常用变量"""
self.url = 'http://tieba.baidu.com/f?kw={}&pn={}'
self.headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1'}
def get_html(self, url):
"""请求功能函数"""
html = requests.get(url=url, headers=self.headers).content.decode('utf-8')
return html
def parse_html(self):
"""解析功能函数"""
pass
def save_html(self, filename, html):
"""数据处理函数"""
with open(filename, 'w') as f:
f.write(html)
def run(self):
"""程序入口函数"""
name = input('请输入贴吧名:')
start = int(input('请输入起始页:'))
end = int(input('请输入终止页:'))
# 编码
params = parse.quote(name)
# 拼接多页的URL地址
for page in range(start, end + 1):
pn = (page - 1) * 50
page_url = self.url.format(params, pn)
# 请求 + 解析 + 数据处理
html = self.get_html(url=page_url)
filename = '{}_第{}页.html'.format(name, page)
self.save_html(filename, html)
# 终端提示
print('第%d页抓取完成' % page)
# 控制数据抓取的频率
time.sleep(random.randint(1, 2))
if __name__ == '__main__':
spider = TiebaSpider()
spider.run()
现在可以看到文件夹里有两个名字为张一山.html的网页了,可以打开看一下,但是还是像我们上一篇说的,可能显示不全的,这里我就不打开了
这个time、random模块导入的原因就是控制抓取量,因为有时候抓取太快了也会被认为是爬虫程序,所以我们随机生成时间段抓取
首先我们介绍正则模块:
# 方法一
r_list=re.findall('正则表达式',html,re.S)
# 方法二
pattern = re.compile('正则表达式',re.S)
r_list = pattern.findall(html)
首先了解正则,以及正则表达式元字符,请自行了解吧,我们就在这里介绍一下这两个东西
1、在整个表达式匹配成功的前提下,尽可能多的匹配 * + ?
2、表示方式:.* .+ .?
1、在整个表达式匹配成功的前提下,尽可能少的匹配 * + ?
2、表示方式:.*? .+? .??
在完整的模式中定义子模式,将每个圆括号中子模式匹配出来的结果提取出来
那我们看一下对于分组必须要知道的事情:
1、在网页中,想要什么内容,就加()
2、先按整体正则匹配,然后再提取分组()中的内容
如果有2个及以上分组(),则结果中以元组形式显示 [(),(),()]
3、最终结果有3种情况
情况1:[]
情况2:['', '', ''] -- 正则中1个分组时
情况3:[(), (), ()] -- 正则中多个分组时
来个案例:
# 从如下html代码结构中完成如下内容信息的提取:
问题1 :[('Tiger',' Two...'),('Rabbit','Small..')]
问题2 :
动物名称 :Tiger
动物描述 :Two tigers two tigers run fast
**********************************************
动物名称 :Rabbit
动物描述 :Small white rabbit white and white
页面的结构如下:
<div class="animal">
<p class="name">
<a title="Tiger"></a>
</p>
<p class="content">
Two tigers two tigers run fast
</p>
</div>
<div class="animal">
<p class="name">
<a title="Rabbit"></a>
</p>
<p class="content">
Small white rabbit white and white
</p>
</div>
import re
html = '''
'''
# 这个就是把没用的删掉,留着必要的,还不能全删了,数据提取哪里就加(.*?),其他无关紧要的要是删的话,就删完了,加上.*?,但是要保留内容的标签,也就是这样的:class="animal"、title=""等等吧
p = re.compile('.*?title="(.*?)".*?content">(.*?).*?',re.S)
r_list = p.findall(html)
for rt in r_list:
print('动物名称:',rt[0].strip())
print('动物描述:',rt[1].strip())
print('*' * 50)
这样就可以提取到了,好了,正则已学完,对于我们的爬虫技术,真的是如虎添翼
接下来就是猫眼电影爬取:
【1】确定URL地址
百度搜索 - 猫眼电影 - 榜单 - top100榜
【2】 爬取目标
所有电影的 电影名称、主演、上映时间
【3】注意 - 程序写完之后未出数据解决思路
1、print(r_list) 发现是空列表
2、print(html) 确认响应内容是否正确(搜索关键字来确定)
3、响应内容不对,跳转到了验证中心
4、F12抓包,找到对应的Cookie和User-Agent,放到程序中的headers参数中再做尝试
5、发现数据抓取成功
第1页:https://maoyan.com/board/4?offset=0
第2页:https://maoyan.com/board/4?offset=10
我们查到了URL地址规律,是0、 10、 20、 30… ,对应着 1、 2、 3、 4… 页
我们得到的结论就是
第n页:offset=(n-1)*10
第三步
<div class="movie-item-info">.*?title="(.*?)".*?class="star">(.*?)</p>.*?releasetime">(.*?)</p>
有想自己尝试编写的我也给出了源代码:
<div class="movie-item-info">
<p class="name"><a href="/films/1200486" title="我不是药神" data-act="boarditem-click" data-val="{movieId:1200486}">我不是药神</a></p>
<p class="star">
主演:徐峥,周一围,王传君
</p>
<p class="releasetime">上映时间:2018-07-05</p> </div>
<div class="movie-item-number score-num">
<p class="score"><i class="integer">9.</i><i class="fraction">6</i></p>
</div>
</div>
可以自己尝试一下改改,这个html码就是所有电影的框架,一般来说不会有另类的改变,也就是说,写一次正则表达式,就是所有的可以用的就是这个:
就以这三个为例子吧,他们的正则表达式都是一样的,至少现在是一样的,以后改不改不敢说,所以,就写出一个电影的表达式,所有的100个电影都可用
第四步
开干吧兄弟,奥利给!!!
import requests
import re
import time
import random
class MaoyanSpider:
def __init__(self):
self.url = 'https://maoyan.com/board/4?offset={}'
self.headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1'}
这一段就是导入模块和定义类和实例化对象,就是以后经常用的,封装起来
def get_html(self, url):
html = requests.get(url=url, headers=self.headers).content.decode('utf-8')
# 直接调用解析函数
self.parse_html(html)
请求响应地址,这个’utf-8’,要看他的编码格式是什么就填什么,因为要是不填的话,可能爬取出来的数据,编码格式是乱码,切记切记!
def parse_html(self, html):
regex = '.*?title="(.*?)".*?class="star">(.*?).*?releasetime">(.*?)'
pattern = re.compile(regex, re.S)
r_list = pattern.findall(html)
# 直接调用数据处理函数
self.save_html(r_list)
这就是我们的正则表达式调用
def save_html(self, r_list):
for r in r_list:
item = {
}
item['name'] = r[0].strip()
item['star'] = r[1].strip()
item['time'] = r[2].strip()
print(item)
这个是抓取的东西就是上面说的电影名称、主演、上映时间,给他用字典整起来,还要按顺序取对应的数据,就是他爬出来的是一个列表,电影名称我们就要取第一个,主演取第二个,上映时间取第三个,还要去掉前后的空格和换行加上strip()函数
def run(self):
for offset in range(0, 91, 10):
page_url = self.url.format(offset)
self.get_html(url=page_url)
# 控制频率
time.sleep(random.randint(1, 3))
再run一下,最后调用main():
if __name__ == '__main__':
spider = MaoyanSpider()
spider.run()
import requests
import re
import time
import random
class MaoyanSpider:
def __init__(self):
self.url = 'https://maoyan.com/board/4?offset={}'
self.headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1'}
def get_html(self, url):
html = requests.get(url=url, headers=self.headers).content.decode('utf-8')
# 直接调用解析函数
self.parse_html(html)
def parse_html(self, html):
regex = '.*?title="(.*?)".*?class="star">(.*?).*?releasetime">(.*?)'
pattern = re.compile(regex, re.S)
r_list = pattern.findall(html)
# 直接调用数据处理函数
self.save_html(r_list)
def save_html(self, r_list):
for r in r_list:
item = {
}
item['name'] = r[0].strip()
item['star'] = r[1].strip()
item['time'] = r[2].strip()
print(item)
def run(self):
for offset in range(0, 91, 10):
page_url = self.url.format(offset)
self.get_html(url=page_url)
# 控制频率
time.sleep(random.randint(1, 3))
if __name__ == '__main__':
spider = MaoyanSpider()
spider.run()