存储方式

之前介绍的都是如何搭建环境、获取数据、解析html,这篇就介绍我使用过的小型数据存储方法。

  1. mysql存储,需安装pymysql库。数据转移不够方便,mysql适合大部分中型小型站点数据的存储。
  2. sqlite3存储,python3标准库。轻便,快速,单数据文件的sqlite3以及它在python3的原生支持的特性,使得它成为非常好的一个存储方案,配合ssd也有不错的性能。
  3. 文本存储(csv)存储,csv为标准库。不需要定义表结构,数据抽取不是很方便,适合数据结构简单的抓取。
    下面是三种存储方式的示例,其中csv存储可以直接运行,mysql和sqlite3只是演示代码的使用,需要和表结构配合才能存储数据。

    import sqlite3
    import pymysql
    import csv
    
    #sqlite3链接数据库
    sqlite_file = 'my.db'
    CONN = sqlite3.connect(sqlite_file)
    CUR = CONN.cursor()
    #sqlite3插入数据
    CUR.execute("INSERT INTO table (linkid,status_code)VALUES('{0[0]}','{0[1]}');".format(data))
    CONN.commit()
    #sqlite3查询数据
    CUR.execute('SELECT linkid FROM table ORDER BY linkid;')
    res = CUR.fetchall()
    
    #mysql链接数据库
    CONN = pymysql.connect(
     host = '127.0.0.1',
     port = 3306,
     user = 'root',
     passwd = 'root',
     db = 'avmopw',
     charset = 'utf8'
    )
    CUR = CONN.cursor()
    #mysql插入数据
    CUR.execute("INSERT INTO table (linkid,status_code,datetime)VALUE('{0[0]}',{0[1]} ,now());".format(data))
    CONN.commit()
    #mysql查询数据
    sql = 'SELECT linkid FROM table ORDER BY linkid;'
    CUR.execute(sql)
    res = CUR.fetchall()
    
    
    #示例数据
    items = [
      ['1','LawnMower','Small Hover mower','Fred','$150','Excellent','2012-01-05'],
      ['2','LawnMower','Ride-on mower','Mike','$370','Fair','2012-04-01'],
      ['3','Bike','BMX bike','Joe','$200','Good','2013-03-22'],
      ['4','Drill','Heavy duty hammer','Rob','$100','Good','2013-10-28'],
      ['5','Scarifier','Quality, stainless steel','Anne','$200','2013-09-14'],
      ['6','Sprinkler','Cheap but effective','Fred','$80','2014-01-06']
    ]
    csv_file = 'csv_file.csv'
    #存储csv文件
    file = open(csv_file,'w',newline='')
    writer = csv.writer(file)
    for item in items:
     writer.writerow(item)
    file.close()
    
    #读取csv文件
    file = open(csv_file)
    datareader = csv.reader(file);
    print(list(datareader))

豆瓣电影

我们尝试抓取豆瓣中电影排名top250,抓取排名,电影名,主演,导演,详情页链接,年份类别,是否可播放,评分,评价人数,和引用评价。这些信息都是在列表页面可以直接得到的,我们只需要抓取并不断翻页抓取下一页,就可以获得所有电影,最后我们使用sqlite3存储数据。

import requests
import sqlite3
import time
from lxml import etree

#获取页面链接
def page_url():
    for page in [x for x in range(0,250,25)]:
        yield 'https://movie.douban.com/top250?start={}&filter='.format(page)

def page_content():
    headers = {
    'Host':'movie.douban.com',
    'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',
    'Referer':'https://movie.douban.com/top250?start=0&filter='
    }
    s = requests.Session()
    s.headers = headers
    data = {}
    for url in page_url():
        print('url',url)
        html_str = s.get(url).text
        html_tree = etree.HTML(html_str)
        #获取页面中所有电影item中class为info的div
        movie_tree = html_tree.xpath('//div[@id="content"]/div/div[@class="article"]/ol/li/div/div[@class="info"]')
        for item in movie_tree:
            item_data = {}
            #电影链接
            item_data['url'] = item.xpath('div[@class="hd"]/a/@href')[0]
            #电影名称
            item_data['name'] = ''.join(item.xpath('div[@class="hd"]/a/span/text()')).replace('\xa0',' ').replace("'","''")
            #是否可播放
            item_data['playable'] = item.xpath('div[@class="hd"]/span/text()')
            if len(item_data['playable'])==1:
                item_data['playable'] = item_data['playable'][0]
            #导演演员
            temp = item.xpath('div[@class="bd"]/p[1]/text()')[0].strip().replace("'","''").split('\xa0\xa0\xa0')
            # print(temp)
            #导演
            item_data['director'] = temp[0].replace("'","''")
            #演员
            if len(temp)==2:
                item_data['actor'] = temp[1].replace("'","''")
            #类别
            item_data['category'] = item.xpath('div[@class="bd"]/p[1]/text()')[1].strip().replace('\xa0',' ').replace("'","''")
            
            #评分
            item_data['rating_num'] = item.xpath('div[@class="bd"]/div/span[2]/text()')[0]
            #多少人评价
            item_data['people_num'] = item.xpath('div[@class="bd"]/div/span[4]/text()')[0]
            #quote
            try:
                item_data['quote'] = item.xpath('div[@class="bd"]/p[2]/span/text()')[0].strip().replace("'","''")
            except:
                item_data['quote'] = ''
            data[len(data)]=item_data
        
        print(len(data))
        time.sleep(3)
    
    #调用sqlite3_save存储数据
    sqlite3_save(data)

def sqlite3_save(data):
    try:
        CONN = sqlite3.connect('douban.db')
        CUR = CONN.cursor()
        CUR.execute('''
        CREATE TABLE "movie_list" (
        "id"  INTEGER,
        "name"  TEXT(200),
        "actor"  TEXT(200),
        "director"  TEXT(200),
        "url"  TEXT(100),
        "category"  TEXT(200),
        "playable"  TEXT(50),
        "rating_num" TEXT(10),
        "people_num" TEXT(10),
        "quote" TEXT(100),
        PRIMARY KEY ("id")
        );
        ''')
    except:
        pass
    
    insert_sql = 'INSERT INTO movie_list (id,name,actor,director,url,category,playable,rating_num,people_num,quote) VALUES '
    for id in data:
        temp_sql = insert_sql + "('{1}','{0[name]}','{0[actor]}','{0[director]}','{0[url]}','{0[category]}','{0[playable]}','{0[rating_num]}','{0[people_num]}','{0[quote]}')".format(data[id],id+1)
        try:
            CUR.execute(temp_sql)
            CONN.commit()
        except:
            pass

if __name__ == '__main__':
    page_content()

以上就是示例,写得很简陋,里面也有很多我踩到的坑,这里把过程写出来。

1、在chrome中打开xpath匹配辅助工具“XPath Helper”,找到想要抓取的最小区域。

2、在这个区域中,分别找到我们想要抓取的信息。

3、执行爬虫,在cmd中查看运行情况

4、使用navicat查看抓取到的数据

一些小细节

replace('\xa0',' ')

\xa0是空格,直接使用replace替换成空格

replace("'","''")

单引号在插入数据库时会报错,我们使用的是sqlite3数据库,所以我们把"'"替换成"''",也就是把一个单引号替换成两个单引号。

def page_url():
    for page in [x for x in range(0,250,25)]:
        yield 'https://movie.douban.com/top250?start={}&filter='.format(page)

这个函数使用了yield,是用于返回下一个url地址的生成器。

最后修改:2021 年 01 月 12 日
如果觉得我的文章对你有用,请随意赞赏