zoukankan      html  css  js  c++  java
  • 案例学python——案例三:豆瓣电影信息入库

     

    闲扯皮
         昨晚给高中的妹妹微信讲题,函数题,小姑娘都十二点了还迷迷糊糊。今天凌晨三点多,被连续的警报声给惊醒了,以为上海拉了防空警报,难不成地震,空袭?难道是楼下那个车主车子被堵了,长按喇叭?开窗看看,好像都不是。好鬼畜的警报声,家里也没装报警器啊,莫不成家里煤气漏了?起床循声而查,报警声的确在厨房,听起来也像屋外,开门也没发现啥异样,莫不成真的是煤气表?下面开始排查,开水,断水,发现没啥异样。打开煤气灶,关闭煤气,也没啥。全屋断电也没啥,全屋都断电了只能说报警声的确来自煤气表。翻出燃气公司的客服电话,那头真的是个妹子,电话里报了下情况和地址,燃气公司说四个小时内给回访。半个小时后警报声自动消失了,一个小时后门铃声响,维修工拿着测气表一通测试,反馈结果说煤气表是新的,也没有明处漏气,让我签了字,就撤了。打开窗,埋头继续睡了。复盘发现自己犯了个大错,当时不应该打开煤气灶和热水器测试的燃气的,万一煤气真的泄露,后果,今天估计医院凉凉了,不对应该是焦焦了。家庭安全不可大意,还是太年轻了。早上6点多楼上装修的工人拆墙砸墙的声音,不胜其扰,早点去公司敲代码吧。先是复盘了一下昨天爬豆瓣的一些小问题,顺带简单解决了。不闲扯了,代码复盘Python爬豆瓣Top250 的信息入库。

    案例一尝试了爬图之后的快感,案例二尝试了白傻呆的数据库操作,案例三就两者整合一下。起因是昨天刚在博客园看到 一篇爬豆瓣的文章 想着刚好能把文章中爬到的信息入数据库,如果在用java操作数据库

    岂不美哉,原谅我习惯于javaWeb开发,因为目前只会java啊。昨天用的Python2.7着实不爽,装了3.7版本。

    效果预览:

    效果一:项目结构

    效果二:数据库信息

     

    效果三:本地存储

     

    思路:第一步:爬取信息  第二步:信息解析   第三步:读写文件    第四步:解析数据入库

    准备工作

     根据解析字段建立对应的数据库,这点因为 博客:一起学爬虫——通过爬取豆瓣电影top250学习requests库的使用 中已经可以看到爬取后解析相关字段,可能有些字段颗粒度不够,在原有基础上再切割切割就ok啦。

     数据库连接配置

     dbMysqlConfig.cnf

    [dbMysql]
    host = localhost
    port = 3306
    user = root
    password = root
    db_name = dou_film

    封装的DBUtils,中间有些小白的错,在里面栽了几个坑:事务,提交,主键自增啥的。其实就用了一个insert()方法,其他方法的正确性可忽略。

    mysqlDBUtils.py

    #!/usr/bin/python3
    # -*- coding:utf-8 -*-
    import pymysql, os, configparser
    from pymysql.cursors import DictCursor
    from DBUtils.PooledDB import PooledDB
    
    
    class Config(object):
        """
        # Config().get_content("user_information")
        配置文件里面的参数
        [dbMysql]
        host = 192.168.1.101
        port = 3306
        user = root
        password = python123
        """
    
        def __init__(self, config_filename="dbMysqlConfig.cnf"):
            file_path = os.path.join(os.path.dirname(__file__), config_filename)
            self.cf = configparser.ConfigParser()
            self.cf.read(file_path)
    
        def get_sections(self):
            return self.cf.sections()
    
        def get_options(self, section):
            return self.cf.options(section)
    
        def get_content(self, section):
            result = {}
            for option in self.get_options(section):
                value = self.cf.get(section, option)
                result[option] = int(value) if value.isdigit() else value
            return result
    
    
    class BasePymysqlPool(object):
        def __init__(self, host, port, user, password, db_name):
            self.db_host = host
            self.db_port = int(port)
            self.user = user
            self.password = str(password)
            self.db = db_name
            self.conn = None
            self.cursor = None
    
    
    class MyPymysqlPool(BasePymysqlPool):
        """
        MYSQL数据库对象,负责产生数据库连接 , 此类中的连接采用连接池实现
            获取连接对象:conn = Mysql.getConn()
            释放连接对象;conn.close()或del conn
        """
        # 连接池对象
        __pool = None
    
        def __init__(self, conf_name=None):
            self.conf = Config().get_content(conf_name)
            super(MyPymysqlPool, self).__init__(**self.conf)
            # 数据库构造函数,从连接池中取出连接,并生成操作游标
            self._conn = self.__getConn()
            self._cursor = self._conn.cursor()
    
        def __getConn(self):
            """
            @summary: 静态方法,从连接池中取出连接
            @return MySQLdb.connection
            """
            if MyPymysqlPool.__pool is None:
                __pool = PooledDB(creator=pymysql,
                                  mincached=1,
                                  maxcached=20,
                                  host=self.db_host,
                                  port=self.db_port,
                                  user=self.user,
                                  passwd=self.password,
                                  db=self.db,
                                  use_unicode=False,
                                  charset="utf8",
                                  cursorclass=DictCursor)
            return __pool.connection()
    
        def getAll(self, sql, param=None):
            """
            @summary: 执行查询,并取出所有结果集
            @param sql:查询SQL,如果有查询条件,请只指定条件列表,并将条件值使用参数[param]传递进来
            @param param: 可选参数,条件列表值(元组/列表)
            @return: result list(字典对象)/boolean 查询到的结果集
            """
            if param is None:
                count = self._cursor.execute(sql)
            else:
                count = self._cursor.execute(sql, param)
            if count > 0:
                result = self._cursor.fetchall()
            else:
                result = False
            return result
    
        def getOne(self, sql, param=None):
            """
            @summary: 执行查询,并取出第一条
            @param sql:查询SQL,如果有查询条件,请只指定条件列表,并将条件值使用参数[param]传递进来
            @param param: 可选参数,条件列表值(元组/列表)
            @return: result list/boolean 查询到的结果集
            """
            if param is None:
                count = self._cursor.execute(sql)
            else:
                count = self._cursor.execute(sql, param)
            if count > 0:
                result = self._cursor.fetchone()
            else:
                result = False
            return result
    
        def getMany(self, sql, num, param=None):
            """
            @summary: 执行查询,并取出num条结果
            @param sql:查询SQL,如果有查询条件,请只指定条件列表,并将条件值使用参数[param]传递进来
            @param num:取得的结果条数
            @param param: 可选参数,条件列表值(元组/列表)
            @return: result list/boolean 查询到的结果集
            """
            if param is None:
                count = self._cursor.execute(sql)
            else:
                count = self._cursor.execute(sql, param)
            if count > 0:
                result = self._cursor.fetchmany(num)
            else:
                result = False
            return result
    
        def insertMany(self, sql, values):
            """
            @summary: 向数据表插入多条记录
            @param sql:要插入的SQL格式
            @param values:要插入的记录数据tuple(tuple)/list[list]
            @return: count 受影响的行数
            """
            count = self._cursor.executemany(sql, values)
            return count
    
        def __query(self, sql, param=None):
            if param is None:
                count = self._cursor.execute(sql)
            else:
                count = self._cursor.execute(sql, param)
            return count
    
        def update(self, sql, param=None):
            """
            @summary: 更新数据表记录
            @param sql: SQL格式及条件,使用(%s,%s)
            @param param: 要更新的  值 tuple/list
            @return: count 受影响的行数
            """
            return self.__query(sql, param)
    
        def insert(self, sql, param=None):
            """
            @summary: 更新数据表记录
            @param sql: SQL格式及条件,使用(%s,%s)
            @param param: 要更新的  值 tuple/list
            @return: count 受影响的行数
            """
            num = self._cursor.execute(sql)
            self._conn.commit()
            return  num
    
        def delete(self, sql, param=None):
            """
            @summary: 删除数据表记录
            @param sql: SQL格式及条件,使用(%s,%s)
            @param param: 要删除的条件 值 tuple/list
            @return: count 受影响的行数
            """
            return self.__query(sql, param)
    
        def begin(self):
            """
            @summary: 开启事务
            """
            self._conn.autocommit(0)
    
        def end(self, option='commit'):
            """
            @summary: 结束事务
            """
            if option == 'commit':
                self._conn.commit()
            else:
                self._conn.rollback()
    
        def dispose(self, isEnd=1):
            """
            @summary: 释放连接池资源
            """
            if isEnd == 1:
                self.end('commit')
            else:
                self.end('rollback')
            self._cursor.close()
            self._conn.close()
    
    
    if __name__ == '__main__':
        mysql = MyPymysqlPool("dbMysql")
        sqlAll = "select * from seckill;"
        result = mysql.getAll(sqlAll)
        print(result)
        # 释放资源
        mysql.dispose()

    爬豆瓣页面数据解析,比较佩服的是里面爬取用的是正则表达式去匹配的,这点只能看个门道,平时用正则表达式都是百度匹配的,测试ok就用了。对原有豆瓣做了一些改动,使其合乎我的想法,虽然想法以后回头看也会诸多问题,先搞出来再说吧。

    其实导包,类引入,为难了自己一下下。其他都还算顺利。

    douFilm.py

     #coding=utf-8
    import requests
    import re
    import json
    import importlib
    import os
    dbUtils = importlib.import_module('mysqlDBUtils')
    
    
    # 定义图片存储位置
    global save_path
    save_path = 'D:/doubanfilm'
    
    
    # 创建文件夹
    def createFile(file_path):
        if os.path.exists(file_path) is False:
            os.makedirs(file_path)
        # 切换路径至上面创建的文件夹
        os.chdir(file_path)
    
    
    def parse_html(url):
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0"}
        response = requests.get(url, headers=headers)
        text = response.text
        regix = '<div class="pic">.*?<em class="">(.*?)</em>.*?<img.*?src="(.*?)" class="">.*?div class="info.*?class="hd".*?class="title">(.*?)</span>.*?class="other">' 
                '(.*?)</span>.*?<div class="bd">.*?<p class="">(.*?)<br>(.*?)</p>.*?class="star.*?<span class="(.*?)"></span>.*?' 
                'span class="rating_num".*?average">(.*?)</span>'
        results = re.findall(regix, text, re.S)
        mysql = dbUtils.MyPymysqlPool("dbMysql")
        for item in results:
            filepath = down_image(item[1],headers = headers)
            print("文件路径"+filepath)
            print(item)
            # item[2] 电影主流名字 item[3] 电影别名
            film_name =  item[2] + ' ' + re.sub('&nbsp;','',item[3])
            info = re.sub('&nbsp;','',item[4].strip()).split(":")
            # 导演
            director = info[1].split('')[0]
            # 主演
            print(len(info))
            if len(info) > 2:
                actor = info[2]
            else:
                actor = "..."
            score_mark = star_transfor(item[6].strip()) + '/' + item[7] + ''
            rank_num = item[0]
            print(film_name)
            # 写sql 语句
            sql = 'insert into film (film_name,director,actor,score_mark,rank_num,filepath) value("' + film_name + '","' + director + '","' + actor + '","' + score_mark + '","' + rank_num + '","'+filepath+'")'
            # 执行插入
            result = mysql.insert(sql)
            yield {
                '电影名称' : film_name,
                '导演和演员' :  director,
                '评分': score_mark,
                '排名' : rank_num
            }
        mysql.dispose()
    def main():
        for offset in range(0, 250, 25):
            url = 'https://movie.douban.com/top250?start=' + str(offset) +'&filter='
            for item in parse_html(url):
                # 将每个条目写入txt
                write_movies_file(item)
    
    def write_movies_file(str):
        with open('douban_film.txt','a',encoding='utf-8') as f:
            f.write(json.dumps(str,ensure_ascii=False) + '
    ')
    
    def down_image(url,headers):
        r = requests.get(url,headers = headers)
        createFile(save_path)
        filepath = save_path +'/'+ re.search('/public/(.*?)$', url, re.S).group(1)
        print("下载的海报名字"+filepath)
        with open(filepath,'wb') as f:
             f.write(r.content)
        return filepath
    def star_transfor(str):
        if str == 'rating5-t':
            return '五星'
        elif str == 'rating45-t' :
            return '四星半'
        elif str == 'rating4-t':
            return '四星'
        elif str == 'rating35-t' :
            return '三星半'
        elif str == 'rating3-t':
            return '三星'
        elif str == 'rating25-t':
            return '两星半'
        elif str == 'rating2-t':
            return '两星'
        elif str == 'rating15-t':
            return '一星半'
        elif str == 'rating1-t':
            return '一星'
        else:
            return '无星'
    
    if __name__ == '__main__':
        main()

     一切还算顺利,注释都在代码里写明了,应该比较好理解。运行的时候,直接运行douFilm.py就ok了。项目很简单 gitHub地址:https://github.com/islowcity/doufilm.git

  • 相关阅读:
    结对
    汉堡 结对2.0
    《构建之法》第四章读后感
    复利计算单元测试
    实验一 命令解释程序的编写
    《构建之法》读后感
    复利计算 2.0
    了解和熟悉操作系统
    学习进度条
    perl的贪婪和非贪婪模式
  • 原文地址:https://www.cnblogs.com/slowcity/p/9988194.html
Copyright © 2011-2022 走看看