Python爬虫学习案例之抓取猫眼电影排行Top100

目前在自学python爬虫,接下来运用学习了的requests库和正则表达式实操一下Python爬虫初学者经常训练的一个小实战案例——爬取猫眼电影排行Top100

抓取分析

首先我们打开抓取的目标站点https://maoyan.com/board/4

在这里插入图片描述

同时此时页面的URL为https://maoyan.com/board/4?offset=0

在这里插入图片描述

我们将网页滚到最下方,发现有分页的列表,直接点击第2页,观察页面的URL和内容发生了变化,URL变为了https://maoyan.com/board/4?offset=10,目前显示的排名为11~20的电影。

在这里插入图片描述

我们点击第3页,URL变为了https://maoyan.com/board/4?offset=20,显示的排名为21~30的电影。
在这里插入图片描述
因此我们可以发现规律,offset代表偏移量,如果偏移量为n,则显示的电影序号为n+1到n+10,每页显示10个。所有想要获取Top100的电影,需要循环10次。

接下来就i是分析首页的源代码了,在开发者工具中的Network监听组件中查看源代码。如图所示
在这里插入图片描述
也可以通过request.get()方法获取首页的源代码

import requests
import re

def get_one_page(url):
   headers = {
      'User-Agent':'Mozilla/5.0',
      }
   response = requests.get(url,headers=headers)
   if response.status_code == 200:
      return response.text
   else:
      return None

def main(offset):
   base_url = 'https://maoyan.com/board/4?offset='
   url = base_url+str(offset)
   html = get_one_page(url)
   print(html)

if __name__=='__main__':
   main(0)

输出如下:(取了其中一部分)

                <dd>
                        <i class="board-index board-index-1">1</i>
    <a href="/films/1375" title="活着" class="image-link" data-act="boarditem-click" data-val="{movieId:1375}">
      <img src="//s3plus.meituan.net/v1/mss_e2821d7f0cfe4ac1bf9202ecf9590e67/cdn-prod/file:5788b470/image/loading_2.e3d934bf.png" alt="" class="poster-default" />
      <img data-src="https://p0.meituan.net/movie/4c41068ef7608c1d4fbfbe6016e589f7204391.jpg@160w_220h_1e_1c" alt="活着" class="board-img" />
    </a>
    <div class="board-item-main">
      <div class="board-item-content">
              <div class="movie-item-info">
        <p class="name"><a href="/films/1375" title="活着" data-act="boarditem-click" data-val="{movieId:1375}">活着</a></p>
        <p class="star">
                主演:葛优,巩俐,牛犇(bēn)
        </p>
<p class="releasetime">上映时间:1994-05-17(法国)</p>    </div>
    <div class="movie-item-number score-num">
<p class="score"><i class="integer">9.</i><i class="fraction">0</i></p>        
    </div>

      </div>
    </div>

                </dd>

可以看到,一部电影的信息对应的源代码就是一个dd节点,我们的目标是提取电影的排名、名称、时间、评分、图片等信息。接下来就是设计正则表达式了

扫描二维码关注公众号,回复: 11478821 查看本文章

正则提取

排名

根据上文源代码,我们可以发现排名信息是在class为board-index的i节点内<i class="board-index board-index-1">1</i>,那么我们就设计正则表达式为<dd>.*?board-index.*?>(\d+)</i>

图片

同样的,上述的a节点中有两个img节点对应的是图片的信息,第二个img节点的data-src属性是图片的链接<img data-src="https://p0.meituan.net/movie/4c41068ef7608c1d4fbfbe6016e589f7204391.jpg@160w_220h_1e_1c" alt="活着" class="board-img" />,因此更新正则表达式为<dd>.*?board-index.*?>(\d+)</i>.*?data-src="(.*?)"

名称

电影的名称在后面的p节点内,class为name,即<p class="name"><a href="/films/1375" title="活着" data-act="boarditem-click" data-val="{movieId:1375}">活着</a></p>。所以可以用name做一个标志位,然后进一步提取到其内a节点的正文内容,此时正则表达式改写如下:<dd>.*?board-index.*?>(\d+)</i>.*?data-src="(.*?)".*?name.*?a.*?>(.*?)</a>

主演

主演提取思路同上,根据

    <p class="star">
            主演:葛优,巩俐,牛犇(bēn)
    </p>

则修改正则表达式为:<dd>.*?board-index.*?>(\d+)</i>.*?data-src="(.*?)".*?name.*?a.*?>(.*?)</a>.*?star.*?>(.*?)</p>

时间

时间提取思路同上,根据<p class="releasetime">上映时间:1994-05-17(法国)</p>修改正则表达式为:<dd>.*?board-index.*?>(\d+)</i>.*?data-src="(.*?)".*?name.*?a.*?>(.*?)</a>.*?star.*?>(.*?)</p>.*?releasetime.*?>(.*?)</p>

评分

评分提取思路同上,最终正则表达式为:<dd>.*?board-index.*?>(\d+)</i>.*?data-src="(.*?)".*?name.*?a.*?>(.*?)</a>.*?star.*?>(.*?)</p>.*?releasetime.*?>(.*?)</p>.*?integer.*?>(.*?)</i>.*?fraction.*?>(.*?)</i>.*?</dd>

接下来就是编写提取页面信息的时候啦!

总代码如下:

import requests
import json
import time
import re
import random

def get_one_page(url):
   
   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',
      },{
      'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0',
      },{
      'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50',
      }]
   count = random.randint(0,2)
   response = requests.get(url,headers=headers[count])
   if response.status_code == 200:
      return response.text
   else:
      return None

def parse_one_page(html):
   pattern = re.compile('<dd>.*?board-index.*?>(\d+)</i>.*?data-src="(.*?)".*?name.*?a.*?>(.*?)</a>.*?star.*?>(.*?)</p>.*?releasetime.*?>(.*?)</p>.*?integer.*?>(.*?)</i>.*?fraction.*?>(.*?)</i>.*?</dd>',re.S)
   items = re.findall(pattern,html)
   for item in items:
      yield{
         'index':item[0],
         'image_url':item[1],
         'title':item[2].strip(),
         'actor':item[3].strip()[3:],
         'time':item[4].strip()[5:],
         'score':item[5].strip()+item[6].strip()
         }

def write_to_file(content):
   with open('result.txt','a',encoding='utf-8') as f:
      f.write(json.dumps(content,ensure_ascii=False)+'\n')
      
def main(offset):
   base_url = 'https://maoyan.com/board/4?offset='
   url = base_url+str(offset)
   html = get_one_page(url)
   for item in parse_one_page(html):
      print(item)
      write_to_file(item)

if __name__=='__main__':
   for i in range(10):
      main(offset = i*10)
      time.sleep(3)

下面针对以上程序进行简单讲解,中心思想就是循环调用main()函数,通过更改offset偏移量来对10个页面进行遍历访问,访问一个页面后如果响应状态码为200则返回响应信息,并通过parse_one_page(html)对返回的信息进行正则提取以及结果处理,同时将获取的结果写入文件。

其中parse_one_page(html)中的下面的代码可能有些不好理解

   for item in items:
      yield{
         'index':item[0],
         'image_url':item[1],
         'title':item[2].strip(),
         'actor':item[3].strip()[3:],
         'time':item[4].strip()[5:],
         'score':item[5].strip()+item[6].strip()
         }

因为函数中带有yield,我们可以把它(该函数)看作一个生成器,并且当执行到yield的时候,函数将会将item的数据处理为字典类型打包返回,返回给了main(offset)的print(item),然后将该部分item写入文件,当执行下一次 for item in parse_one_page(html)时,程序跳入parse_one_page(html)的上次离开的地方,即执行下一次parse_one_page(html)的for item in items循环,而并不是从函数开头执行

如下图:
在这里插入图片描述
对于write_to_file(content)函数,这里就是通过JSON库的dumps()方法实现字典的序列化,并指定ensure_ascii参数为Fasle,这样就可以保证输出结果是中文形式而不是Unicode编码。

最后,成功爬取到了Top100,是真真切切感受到了爬虫的强大,加利用真的能简化很多事情呢!

保存的result.txt文件输出打印信息
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_42642142/article/details/107655245