零基础爬虫实战(Python):抓取豆瓣电影TOP250

2019-04-30  本文已影响0人  木草zhg

​ 学习了《简明Python教程》,然后想着实战一下,搜索了一些资料,然后对豆瓣电影排行250进行了一个抓取,后续还会对数据进行一些分析。

​ 这篇文章主要是对抓取豆瓣电影top250过程的一个梳理,方便日后自己查阅,也希望可以方便到有需要的人。

​ 下面是整个抓取过程的思维导图:

爬取豆瓣电影TOP250.png

1. 生成URL信息

​ 首先观察豆瓣电影TOP250的网页地址,多点开几页,就能发现规律。每一页都是展示了25个电影。

2. 分析网页标签

​ 这个就是对网页html文件的解析了,可以先在网页中浏览目标信息,然后在网页的源码中通过搜索查找到相应的标签。最后一定要搞清楚定位每一个目标信息标签的方式。

​ 我在这是采用的是BeautifulSoup对html进行的解析。

3. 请求网页数据

​ 我是使用urllib.request中的urlopen()方法对网页进行请求的。

urlopen(url).read().decode('utf-8')

​ urlopen(url)获取到的是HTTPResponse对象,然后调用read()方法获得网页的字节数据,字节数据没法直接处理,所以最后调用decode('utf-8')方法将获取的字节对象编码成文本字符串(string)

4. 存储信息

​ 先把数据生成pandas库的DataFrame对象,然后调用其to_csv()方法将数据存储到csv文件中,非常的方便。

完整的代码

​ 算是自己完成了第一个小的爬虫程序,以后会在此基础上不断的扩展与完善。下面是获取豆瓣电影TOP250的Python完整代码。

# encoding=utf-8

from collections import defaultdict
from urllib.request import urlopen
from bs4 import BeautifulSoup
import pandas as pd
import time
import re


class DoubanMovieTop250:
    # 生成url和相应存储最终数据的容器
    def __init__(self):
        self.top250_urls = ['https://movie.douban.com/top250?start={0}&filter='.format(i * 25) for i in range(10)]
        self.data = defaultdict(list)
        self.columns = ['title', 'link', 'score', 'score_cnt', 'top_no', 'directors', 'writers', 'actors', 'types',
                        'edit_location', 'language', 'dates', 'play_location', 'length', 'rating_per',
                         'betters', 'had_seen', 'want_see', 'tags', 'review', 'ask', 'discussion']
        self.df=None

    def get_info(self):
        for url in self.top250_urls:
            # 将获取的网页封装成BeautifulSoup
            print(url)
            html = urlopen(url).read().decode('utf-8')

            bsobj = BeautifulSoup(html)

            # 开始解析网页中的数据
            main = bsobj.find('ol', {'class': 'grid_view'})


            # 标题及链接信息
            title_obj = main.findAll('div', {'class': 'hd'})
            titles = [ i.find('span', {'class': 'title'}).text for i in title_obj]
            links = [ i.find('a')['href'] for i in title_obj]

            # 评分信息
            score_obj = main.findAll('div', {'class': 'star'})
            scores = [i.find('span', {'class': 'rating_num', 'property': 'v:average'}).text for i in score_obj]
            score_cnts = [ i.findAll('span')[-1].text for i in score_obj]

            # 将解析到的数据存入到容器中
            for title, link, score, score_cnt in zip(titles, links, scores, score_cnts):
                self.data[title].extend([title, link, score, score_cnt])
                # 解析更多、更详细的信息
                more_data = self.get_more_info(link)
                self.data[title].extend(more_data)
                print(self.data[title])
                print(len(self.data))
                time.sleep(1)

    def get_more_info(self, link):
        html = urlopen(link).read().decode('utf-8')
        bsobj = BeautifulSoup(html)

        # 榜单排名 top_no
        top_no = bsobj.find('span', {'class': 'top250-no'}).text

        # 导演 directors
        main = bsobj.find('div', {'id': 'info'})
        directors = [i.text for i in main.findAll('a', {'rel': 'v:directedBy'})]

        # 编剧 writers 可能没有编剧
        try:
            writers = [i.text for i in main.findAll('span', {'class': 'attrs'})[1].findAll('a')]
        except Exception as ex:
            writers = []
            print(ex)

        # 主演 actors 可能没有主演
        try:
            actors_obj = main.findAll('a', {'rel': 'v:starring'})
            actors = [i.text for i in actors_obj]
        except Exception as ex:
            actors = []
            print(ex)

        # 类型 types
        types_obj = main.findAll('span', {'property': 'v:genre'})
        types = [i.text for i in types_obj]

        # 制片地区 edit_location
        pattern = re.compile('地区:(.*)\n语言', re.S)
        edit_location = re.findall(pattern, main.text)[0]

        # 语言 language
        pattern2 = re.compile('语言:(.*)\n上映日期', re.S)
        language = re.findall(pattern2, main.text)[0]


        # 上映日期和地区  dates play_location
        date_boj = main.findAll('span', {'property': 'v:initialReleaseDate'})
        dates = [i['content'].split('(')[0] for i in date_boj]
        play_location = [i['content'].split('(')[1] for i in date_boj]

        # 片长 length
        length = main.find('span', {'property': 'v:runtime'})['content']

        # 5星到1星的比例 rating_per 一共是5个数字
        rating_per = [i.text for i in bsobj.findAll('span', {'class': 'rating_per'})]

        # 好于 betters
        better_obj = bsobj.find('div', {'class': 'rating_betterthan'}).findAll('a')
        betters = [i.text for i in better_obj]


        # 想看/看过 had_seen want_see
        fit_obj = bsobj.find('div', {'class': 'subject-others-interests-ft'})
        had_seen = fit_obj.find('a').text[:-3]
        want_see = fit_obj.findAll('a')[1].text[:-3]

        # 标签 tags
        tags = [i.text for i in bsobj.find('div', {'class': 'tags-body'}).findAll('a')]

        # 短评 review  (有多少个短评)
        review = bsobj.find('div', {'id': 'comments-section'}).find('h2').find('a').text

        # 问题 ask (有多少个问题)
        ask = bsobj.find('div', {'id': 'askmatrix'}).find('a').text.strip()

        # 讨论 discussion (有多少个讨论)
        discussion = bsobj.find('p', {'class': 'pl', 'align':'right'}).find('a').text.strip().split('(')[1][2:-2]

        more_data=[top_no, directors, writers, actors, types, edit_location, language, dates, play_location, length, rating_per, betters, had_seen, want_see,tags, review, ask, discussion]
        return more_data

    # 存储数据到csv文件
    def dump_data(self):
        data = []
        for title, value in self.data.items():
            data.append(value)
            self.df=pd.DataFrame(data, columns=self.columns)
            self.df.to_csv('exercise_douban_movie_Top250.csv')


if __name__ == '__main__':
    douban = DoubanMovieTop250()
    douban.get_info()
    douban.dump_data()

参看资料:零基础Python爬虫实战:豆瓣电影TOP250

上一篇下一篇

猜你喜欢

热点阅读