Python爬虫系列2(百度贴吧案例,猫眼电影案例,正则匹配)

贴吧案例

今天我们来爬取一下百度贴吧的数据,
主要步骤为以下五步
1.查看页面是否为静态页面
2.找到url规律
3.获取网页内容
4提取所需数据
5.保存(数据库、本地)

查看是否为静态页面

在这里提一点,目前我们抓取的数据都是静态页面的,所有第一件事就是要确认你所抓取的页面是否为静态 的,这点你可以通过查看网页源代码查看,你可以找到
这是贴吧的页面
在这里插入图片描述这是源代码

在这里插入图片描述通过上图对比我们可以看到,像我们看到的一些标题之类的在源代码中都有体现,我们可以进行抓取
下面我们再来看一个动态加载的页面![
](https://img-blog.csdnimg.cn/20210227144940782.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ4MzIyMzk0,size_16,color_FFFFFF,t_70)

这是腾讯招聘 我搜索Python的一个页面,上面有职位信息,地点等,我们再来看看它的源代码

在这里插入图片描述从图中可以看到,源代码中并不存在招聘岗位等信息,因为此类页面是动态加载的,需要通过动态加载的办法去抓取,这个先说到这儿,我们进行抓取一定要先去看看网页源代码里是静态还是动态,页面中是否有我们需要的东西,你也可以自行查看一下其中的规律
下面我们开始百度贴吧的抓取

我们进行抓取的第一步是确定我们抓取的页面,以及页面是否存在我们需要的信息
第二步就是找到url规律,即你所抓取的地址。

找到url规律

我们在贴吧里搜索英雄联盟吧
在这里插入图片描述可以看到这样一个页面
注意看它的url

在这里插入图片描述kw是关键词IE是编码格式还有一个pn我们点击下一页到第二页看一下有什么不同
在这里插入图片描述此时的pn变成了50,我们再看一下第三页,发现pn变成了100,即pn就是页码以50递进,这样我们确定了url的规律

第一页 https://tieba.baidu.com/f?kw=&pn=0
第二页 https://tieba.baidu.com/f?kw=&pn=50
第三页 https://tieba.baidu.com/f?kw=&pn=100
第n页 pn=(n-1)*50
其中ie被我省略了 ,访问可以得到同样效果

获取页面

我们直接上代码

from urllib import request, parse
import random
import time


class TiebaSpider(object):
    def __init__(self):
        self.url = 'https://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_page(self, url):
        req = request.Request(url=url, headers=self.headers)
        res = request.urlopen(req)
        html = res.read().decode()
        return html

    # 提取数据
    def parse_page(self):
        pass

    # 保存
    def write_page(self,filename, html):
        with open(filename, 'w',encoding='utf-8') as f:
            f.write(html)

    # 入口函数
    def run(self):
        name = input('请输入贴吧名:')
        start = int(input('请输入起始页:'))
        end = int(input('请输入终止页:'))
        kw = parse.quote(name)
        # 拼接+获取内容+保存
        for i in range(start, end + 1):
            pn = (i - 1) * 50
            url = self.url.format(kw, pn)
            html = self.get_page(url)
            filename = '{}-第{}页.html'.format(name, i)
            self.write_page(filename, html)
            print('第%d页抓取成功' % i)
            # 每爬取一个页面随机休眠1-3秒
            time.sleep(random.randint(1, 3))


if __name__ == '__main__':
    spider = TiebaSpider()
    spider.run()

这个流程其实和我在第一篇中所写没有太大区别,只是让代码更加规范化,实际上的原理是一样的,这里就不一一细说,有不明白的可以回去看看第一篇入门,这里我们使用sleep每抓取一个页面停留一个随机时间,这样做的原因是为了防止被网页封禁,为什么不使用一个固定的时间,是因为人访问页面不可能做到那么精确,当然嫌慢的同学也可以把这个去掉,其中关于数据提取,我们后面再说,在这里,输入你所要爬取的贴吧名以及爬取页数,就可以得到相应的网页
在这里插入图片描述如图,我抓取的是英雄联盟吧1到3页的内容
下面我们做一下优化
首先是User_Agent,在第一篇入门中我们有说到userAgent的作用,这里我们对它再改善,如果我们使用一个user-agent大量访问一个网站,会引起网站的注意,为了更好的伪装,我们新建一个useragents的py文件
在这里插入图片描述在网上找到不同的user-agent放到里面,然后把这个文件导入到刚才所写的程序中,就可以实现以不同的useragent访问网页,

修改后的代码如下

from urllib import request, parse
import random
import time
from useragerts import ua_list


class TiebaSpider(object):
    def __init__(self):
        self.url = 'https://tieba.baidu.com/f?kw={}&pn={}'

    # 获取响应内容
    def get_page(self, url):
        headers = {'User-Agent': random.choice(ua_list)}
        req = request.Request(url=url, headers=headers)
        res = request.urlopen(req)
        html = res.read().decode()
        return html

    # 提取数据
    def parse_page(self):
        pass

    # 保存
    def write_page(self, filename, html):
        with open(filename, 'w', encoding='utf-8') as f:
            f.write(html)

    # 入口函数
    def run(self):
        name = input('请输入贴吧名:')
        start = int(input('请输入起始页:'))
        end = int(input('请输入终止页:'))
        kw = parse.quote(name)
        # 拼接+获取内容+保存
        for i in range(start, end + 1):
            pn = (i - 1) * 50
            url = self.url.format(kw, pn)
            html = self.get_page(url)
            filename = '{}-第{}页.html'.format(name, i)
            self.write_page(filename, html)
            print('第%d页抓取成功' % i)
            # 每爬取一个页面随机休眠1-3秒
            time.sleep(random.randint(1, 3))


if __name__ == '__main__':
    begin = time.time()

    spider = TiebaSpider()
    spider.run()
    stop = time.time()
    print('执行时间:%2.f' % (stop - begin))

Python有一个专门的模块用来获取user-agent fake_useragent 需要装这个插件来使用,这个有一个弊端,它是从网站上获取userAgent的如果网络不好或者其他原因可能出现一个问题,装插件的命令如下
在控制台输入一下命令

python -m pip install fake_useragent

在这里插入图片描述模块就成功获取 了
使用也很简单,导入一个,模块,创建一个对象,然后随机获取就可以使用了
在这里插入图片描述第一次可能有点慢 ,还可能报错,可以多试几次,成功后就可以快速获取了 如下图
在这里插入图片描述改动后代码如下

from urllib import request, parse
import random
import time
from fake_useragent import UserAgent



class TiebaSpider(object):
    def __init__(self):
        self.url = 'https://tieba.baidu.com/f?kw={}&pn={}'
        # 生成随机UserAgent

    def get_headers(self):
        ua = UserAgent()
        headers = {'User_Agent': ua.random}
        return headers

    # 获取响应内容
    def get_page(self, url):
        headers = self.get_headers()
        req = request.Request(url=url, headers=headers)
        res = request.urlopen(req)
        html = res.read().decode()
        return html



    # 提取数据
    def parse_page(self):
        pass

    # 保存
    def write_page(self, filename, html):
        with open(filename, 'w', encoding='utf-8') as f:
            f.write(html)

    # 入口函数
    def run(self):
        name = input('请输入贴吧名:')
        start = int(input('请输入起始页:'))
        end = int(input('请输入终止页:'))
        kw = parse.quote(name)
        # 拼接+获取内容+保存
        for i in range(start, end + 1):
            pn = (i - 1) * 50
            url = self.url.format(kw, pn)
            html = self.get_page(url)
            filename = '{}-第{}页.html'.format(name, i)
            self.write_page(filename, html)
            print('第%d页抓取成功' % i)
            # 每爬取一个页面随机休眠1-3秒
            time.sleep(random.randint(1, 3))


if __name__ == '__main__':
    begin = time.time()

    spider = TiebaSpider()
    spider.run()
    stop = time.time()
    print('执行时间:%2.f' % (stop - begin))

这样就成功改造了,
在这里提一下,在爬取过程中,你可能会遇到这样的错误

Connection Reset  error [error 104]:

这是远程主机怀疑你是爬虫,断掉了和你的连接,这里我们可以通过以下方式处理,我们捕获异常,连接失败再次连接,具体代码如下
在这里插入图片描述如果报错就休眠0.5秒重试
关于这个模块仔细研究以下,后面其实大致流程也是这样,打好基础

正则解析模块

正则是我们学习爬虫必须要了解的一个模块,我们提取我们所需的数据是要用到
这里我们使用的是re模块
我们先做一个简单的练习

import re
r_list = re.findall('AB', 'ABCABCABCD')
print(r_list)

可以得到结果
在这里插入图片描述还有另外一种方式

import re
parttern = re.compile('AB')
r_list2 = parttern.findall('ABCABCABCD')
print(r_list2)

用一个变量去接收,效果是一样的
总结下来是这样


r_list = re.findall('正则表达式', html,re.S)

parttern = re.compile('正则表达式')
r_list2 = parttern.findall(html,re.S)

其中re.S的作用是一旦正则表达式中出现 . 就无法匹配 /n,让点能匹配/n 就这一个作用
在字符串a中,包含换行符\n,在这种情况下:
如果不使用re.S参数,则只在每一行内进行匹配,如果一行没有,就换下一行重新开始。
而使用re.S参数以后,正则表达式会将这个字符串作为一个整体,在整体中进行匹配。
正则表达式元字符
在这里插入图片描述这里并非所有的 只是一些常用的
匹配任意字符的正则表达式

re.compile('.', re.S)
re.compile('[/s/S]')

贪婪匹配和非贪婪匹配
在这里插入图片描述这是贪婪和非贪婪的表示方式,就这样可能很难理解我们来个例子

import re
html = '''
<div><p>长风破浪会有时</p><div>
<div><p>直挂云帆济沧海</p><div>
'''
# 贪婪匹配
pattern = re.compile('<div><p>.*</p><div>', re.S)
r_lsit = pattern.findall(html)
print(r_lsit)

我们自己写一个假网页进行匹配,看结果
在这里插入图片描述我们只得到一个元素 ,即中间的都被匹配进去了
我们再来看非贪匹配

import re
html = '''
<div><p>长风破浪会有时</p><div>
<div><p>直挂云帆济沧海</p><div>
'''
# 非贪婪匹配
pattern = re.compile('<div><p>.*?</p><div>', re.S)
r_lsit2 = pattern.findall(html)
print(r_lsit2)

在这里插入图片描述匹配最近的一个元素,在实际使用中更多的是用到非贪婪匹配,我们可以提取出一个一个的元素,而非贪婪就相当于吧符合的整个一块提取了
那我们只想要其中的文字,不需要外面的《div》之类的怎么办,很简单加一个() 看代码

import re
html = '''
<div><p>长风破浪会有时</p><div>
<div><p>直挂云帆济沧海</p><div>
'''
# 贪婪匹配
pattern = re.compile('<div><p>(.*)</p><div>', re.S)
r_lsit = pattern.findall(html)
print(r_lsit)
# 非贪婪匹配
pattern = re.compile('<div><p>(.*?)</p><div>', re.S)
r_lsit2 = pattern.findall(html)
print(r_lsit2)

得到结果如下
在这里插入图片描述第一个是贪婪匹配
第二个是非贪婪匹配
爬虫里面基本上不使用贪婪

正则表达式分组

在完整的模式中定义子模式,将每个圆括号中子模式匹配出来的结果提取出来
是什么意思呢 我们看下面这段代码

import re
s = 'A B C D'
p1 = re.compile('\w+\s+\w')
print(p1.findall(s))
# 匹配完整 ->['A B', 'C D']

p2 = re.compile('(\w)+\s+\w')
print(p2.findall(s))
# 第一步匹配完整 ->['A B', 'C D']
# 第二步匹配()->['A', 'C']

p3 = re.compile('(\w)+\s+(\w)')
print(p3.findall(s))
# 第一步匹配完整 ->['A B', 'C D']
# 第二步匹配()->[('A', 'B'), ('C', 'D')]

结果是不是我们分析那样呢
在这里插入图片描述分组总结

1.在网页中,想要什么内容就加()
2.先按整体正则匹配,然后提取分组()中的内容,
如果有两个及其以上分组()中的内容,则结果以元组形式显示
[(‘苹果’,‘红色’),(‘苹果’,‘绿色’),()]
以此类推

在这里插入图片描述这里多说一点,就好像我们要抓取boss上的岗位和薪资,我们就可以把这两个元素加()
下面我们来一个实例,更好的去理解
我们爬取猫眼电影的T100榜
爬取流程和之前一样,这里就不多说,我们先查看这个页面
找到url规律

https://maoyan.com/board/4?offset=0

url为这个,查找规律,再点开第二页就行了
我们分析页面,我们要抓取的是电影名、主演、以及上映时间
在这里插入图片描述我们检查一下源代码,看一下有没有相关数据
在这里插入图片描述
如图页面是有我们所需要的数据,我们可以多查看几个找到其中规律
在这里插入图片描述我们需要关注的是包裹我们所要信息的div之类的如上图
我们所需要的正则大概是这样的

<div class="movie-item-info">.*?title="(.*?)".*?class="star">(.*?)</p>.*?releasetime">(*?)</p>

仔细研究以下这个正则,相信你会有很大收获,我们确定抓取位置

开始,当然这个可以你自己决定,但一定要包含你所要数据在其中,然后找到我们要的信息title="(.*?)" 我们所要的电影名是title 双引号中间的 我们呢用括号包起来 之后也是一样的,然后直至最后抓取信息结束,然后正则结束,将这个正则和源代码对比起来看一下,相信你很快能理解

下面我们开始写程序抓取

from urllib import request, parse
import random
import time
from fake_useragent import UserAgent
import re


class MaoyanSpider(object):
    def __init__(self):
        self.url = 'https://maoyan.com/board/4?offset={}'
        # 生成随机UserAgent
        self.i = 0

    def get_headers(self):
        ua = UserAgent()
        headers = {'User_Agent': ua.random}
        return headers

    # 获取响应内容
    def get_page(self, url):
        headers = self.get_headers()
        print(url)
        req = request.Request(url=url, headers=headers)
        res = request.urlopen(req)
        html = res.read().decode()
        # 直接调用解析
        self.parse_page(html)

    # 提取数据
    def parse_page(self, html):
        re_bds = '<div class="movie-item-info">.*?title="(.*?)".*?class="star">(.*?)</p>.*?releasetime">(.*?)</p>'
        pattern = re.compile(re_bds, re.S)
        r_list = pattern.findall(html)
        self.write_page(r_list)

    # 保存
    def write_page(self, r_list):
        item = {}
        for r in r_list:
        # .strip用于去掉两端空白部分
            item['name'] = r[0].strip()
            item['star'] = r[1].strip()
            item['time'] = r[2].strip()
            print(item)
            self.i += 1

    # 入口函数
    def run(self):
        for offset in (0, 91, 10):
            url = self.url.format(offset)
            self.get_page(url)
            # 随机休眠
            # random.uniform随机生成浮点数
            time.sleep(random.uniform(1, 2))
        print('数量', self.i)

if __name__ == '__main__':
    begin = time.time()
    spider = MaoyanSpider()
    spider.run()
    stop = time.time()
    print('执行时间:%2.f' % (stop - begin))

这里我们先把数据打印出来,储存到数据库后面再说,我们可以得到结果,成功抓取,总共有十页,猫眼管的有点严,抓了几次才成功,自己可以修改offset的范围来控制抓取页数。正则是这一章的重点,一点要多去理解,才能成功获取到你要的数据
在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值