Python手记-8:python一次性爬取多页数据并存入CSV文件

目录

1. 一次性爬取多页豆瓣绘本数据

2. 爬取多页豆瓣绘本数据存入CSV文件

1. 一次性爬取多页豆瓣绘本数据

Python手记-7中实现了单页爬取数据,本节来试试多页数据爬取,案例背景为豆瓣图书网页实现绘本的多页数据爬取,先看看网址信息:

复制出来:

第一页:https://book.douban.com/tag/%E7%BB%98%E6%9C%AC?start=0&type=T

第二页:https://book.douban.com/tag/%E7%BB%98%E6%9C%AC?start=20&type=T

第三页:https://book.douban.com/tag/%E7%BB%98%E6%9C%AC?start=40&type=T

……

在原网址里的中文字符“绘本”复制出来变成了“%E7%BB%98%E6%9C%AC”,这个是URL编码问题,用哪个都不影响爬取;

观察推断url变化规律,变化的只是start参数值,且start值是首项为0,公差为20的等差递增数列:

第i页:https://book.douban.com/tag/%E7%BB%98%E6%9C%AC?start=(i-1)* 20&type=T

下面就粗略的试试看:

# -*- coding: utf-8 -*- 
# @Time : 2020/4/27 14:14
# @Author : ChengYu
# @File : requests_getbooks.py

import requests
import re

# 加上headers用来告诉网站这是通过一个浏览器进行的访问
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                         'Chrome/81.0.4044.122 Safari/537.36'}
# page_index起始值为0,爬取20页
for page_index in range(20):
    url = 'https://book.douban.com/tag/%E7%BB%98%E6%9C%AC?start=' + str(page_index * 20) + '&type=T'
    # url = 'https://book.douban.com/tag/绘本?start=' + str(page_index * 20) + '&type=T'

    # 获取网页源码
    res = requests.get(url, headers=headers).text
    # 获取绘本链接、作者信息、书名、评价数、简介
    p_href = '<h2 class="">.*?<a href="(.*?)"'
    href = re.findall(p_href, res, re.S)
    p_info = '<div class="pub">(.*?)</div>'
    info = re.findall(p_info, res, re.S)
    # p_title = '<h2 class="">.*?>(.*?)</a>'
    p_title = '<h2 class="">.*?title="(.*?)"'
    title = re.findall(p_title, res, re.S)
    p_appraise = '<span class="pl">(.*?)</span>'
    appraise = re.findall(p_appraise, res, re.S)
    # p_detail = '<div class="info">.*?<p>(.*?)</p>'
    # detail = re.findall(p_detail, res, re.S)

    for i in range(len(title)):
        title[i] = title[i].strip()
        info[i] = info[i].strip()
        appraise[i] = appraise[i].strip()
        href[i] = href[i].strip()
        # detail[i] = detail[i].strip()
        # 出去换行符
        # detail[i] = re.sub('\n', '', detail[i])
        print(str(i + 1) + '.' + title[i] + ' ' + href[i] + '\n' + info[i] + '\n' + appraise[i])
        
    print('第' + str(page_index + 1) + '页爬取成功')

“run”一下看看结果,书名、链接、作者/出版等信息、评论数了然在列:

2. 爬取多页豆瓣绘本数据存入CSV文件

CSV模块是实现以CSV格式读取和写入表格数据的类,CSV模块主要功能(官文:https://docs.python.org/3.8/library/csv.html):

(1)csv.reader(csvfiledialect='excel'**fmtparams)/csv.writer(csvfiledialect='excel'**fmtparams):csvfile可以是支持迭代器协议并在每次__next__()调用其方法时都返回字符串的任何对象- 文件对象和列表对象均适用,简言之Excel或者CSV文件都可以;其中的Dialects and Formatting参数:

  • Dialect.delimiter:分隔字段的字符,默认为',';
  • Dialect.doublequote:双引号,当单引号已经被定义,并且quoting参数不是QUOTE_NONE的时候,使用双引号表示引号内的元素作为一个元素使用;
  • Dialect.escapechar:当quoting 为QUOTE_NONE时,指定一个字符使的不受分隔符限值,默认为None,将禁用转义;
  • Dialect.lineterminator:行分隔符,默认为'\r\n';
  • Dialect.quotechar:引用符,用于引用包含特殊字符(例如定界符或quotechar)或包含换行符的字段,默认为'"'。
  • Dialect.quoting:控制csv中的引号常量,可选QUOTE_*常量,默认为QUOTE_MINIMAL;
  • Dialect.skipinitialspace:如果为True,则分隔符后的空白将被忽略,默认值为False;
  • Dialect.strict:如果为True,则Error在CSV输入错误时引发异常,默认值为False。

(2)csv.DictReader(f,fieldnames=Nonerestkey=Nonerestval=Nonedialect='excel'*args**kwds)/csv.DictWriter(f, fieldnames, restval='', extrasaction='raise', dialect='excel', *args, **kwds):

  • fieldnames:字段名列表,默认是第一行的数据;
  • restkey:当实际的字段数大于字段名数量时,多出来的字段名称就是这个restkey指定(默认为None);
  • restval:如果非空白行的字段少于字段名,则缺少的值将填充为restval的值(默认为None);
  • extrasaction:当数据中有额外的字段时所采取的操作,默认为’raise’,即抛异常,有时候这种情况比较烦,所以还可以设置成’ignore’直接忽略。

(3)csvreader.fieldnames:字段名称,默认是第一行的数据。

(4)csvwriter.writerow(row)/csvwriter.writerows(rows):单行写入/多行写入。

(5)DictWriter.writeheader():写入字段名称,也就是csv头部信息。

写入用法示例(偷懒了,来源官文):

import csv

with open('names.csv', 'w', newline='') as csvfile:
    fieldnames = ['first_name', 'last_name']
    writer = csv.DictWriter(csvfile, fieldnames=fieldnames)

    writer.writeheader()
    writer.writerow({'first_name': 'Baked', 'last_name': 'Beans'})
    writer.writerow({'first_name': 'Lovely', 'last_name': 'Spam'})
    writer.writerow({'first_name': 'Wonderful', 'last_name': 'Spam'})

读取用法示例(偷懒了,来源官文):

import csv
with open('some.csv', newline='', encoding='utf-8') as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)

学以致用,接下来在前面的案例基础上改动一下,继续爬取豆瓣绘本前10页的书本数据,并稍微美化一下输出,以CSV文件格式保存数据。

# -*- coding: utf-8 -*- 
# @Time : 2020/4/27 14:14 
# @Author : ChengYu 
# @File : requests_getbooks.py

import requests
import re
import csv

# 加上headers用来告诉网站这是通过一个浏览器进行的访问
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                         'Chrome/81.0.4044.122 Safari/537.36'}
# 初始化csv文件
with open('books.csv', 'w', newline='', encoding='utf-8') as csvfile:
    writer = csv.writer(csvfile)
    writer.writerow(['序号', '书名', '评分', '评论人数', '作者/出版社/出版日期/价格', '链接'])
    # bookinfo = []
    count = 1
    # page_index起始值为0,爬取10页
    for page_index in range(10):
        url = 'https://book.douban.com/tag/%E7%BB%98%E6%9C%AC?start=' + str(page_index * 20) + '&type=T'
        # url = 'https://book.douban.com/tag/绘本?start=' + str(page_index * 20) + '&type=T'

        # 获取网页源码
        res = requests.get(url, headers=headers).text
        # 获取绘本链接、作者信息、书名、评价数、简介
        p_href = '<h2 class="">.*?<a href="(.*?)"'
        href = re.findall(p_href, res, re.S)
        p_info = '<div class="pub">(.*?)</div>'
        info = re.findall(p_info, res, re.S)
        # p_title = '<h2 class="">.*?>(.*?)</a>'
        p_title = '<h2 class="">.*?title="(.*?)"'
        title = re.findall(p_title, res, re.S)
        p_grade = '<span class="rating_nums">(.*?)</span>'
        grade = re.findall(p_grade, res, re.S)
        p_appraise = '<span class="pl">(.*?)</span>'
        appraise = re.findall(p_appraise, res, re.S)

        # p_detail = '<div class="info">.*?<p>(.*?)</p>'
        # detail = re.findall(p_detail, res, re.S)

        for j in range(len(title)):
            title[j] = title[j].strip()
            info[j] = info[j].strip()
            appraise[j] = appraise[j].strip().replace('(', '').replace('人评价)', '')
            grade[j] = grade[j].strip()
            href[j] = href[j].strip()
            writer.writerow([count, title[j], grade[j], appraise[j], info[j], href[j]])
            # print(title[i], info[i], href[i], appraise[i])
            # bookinfo.append([title[i], info[i], href[i], appraise[i]])
            # print('第' + str(i + 1) + '页爬取成功')
            count += 1

    # 注意csvfile.close()缩进问题,这应该是与for同级别,否则可能造成:ValueError: I/O operation on closed file.
    csvfile.close()

“run”之后会在当前project目录生成books.csv,内容显示如下,序号不再是1-20的循环,而是从1一直递增到爬取结束,评论人数从(558486人评价)变为558486,增加评分列爬取,当然也可以增加其他的爬取内容,无外乎就是正则和re库方法的使用。

叮!浅尝辄止,这个主题暂时下课! 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章