爬虫 | 百行代码爬取14.5W条豆瓣图书信息

前言

先上一波爬取的结果:

数据库中部分截图

实战

引入类库

import requests
from bs4 import BeautifulSoup
from requests.exceptions import RequestException
from urllib.parse import urlencode
import pymongo
import numpy as np
import time
from faker import Faker

分析页面请求

分析目标页面

打开开发者模式,查看链接

点击任意标签,分析页面请求 分别请求不同的标签页面,分析请求链接,可以发现如下规律:

tag_url = 'https://book.douban.com' + 标签页中a标签括起来的内容

由此,我们可以构建以下代码,以获取标签页面所有标签链接:

# 解析总标签页面,并拼接获得所有标签页页面链接
def splice_tags_indexhtml(html):
    url = 'https://book.douban.com'
    book_tags = []
    tags_url = []
    soup = BeautifulSoup(html, 'lxml')
    tagurl_lists = soup.select('#content > div > div.article > div > div > table > tbody > tr > td > a')
    for tag_url in tagurl_lists:
        # 获取全部标签的a标签内容,并拼接到一起
        book_tags += [tag_url.attrs["href"]]
    for book_tag in book_tags:
        tags_url.append([url + book_tag])
    return tags_url

我们进入单个标签页面,分析图书列表页面,解析我们需要存储的字段 我们通过bs4解析我们需要的字段,如:出版时间,作者/译者,豆瓣评分,售价,评价人数等。

# 解析单个tag页面下单页的信息
def parse_tag_page(html):
    try:
        soup = BeautifulSoup(html,"lxml")
        tag_name = soup.select('title')[0].get_text().strip()
        list_soup = soup.find('ul', {'class': 'subject-list'})
        if list_soup == None:
            print('获取信息列表失败')
        else:
            for book_info in list_soup.findAll('div', {'class': 'info'}):
                # 书名
                title = book_info.find('a').get('title').strip()
                # 评价人数
                people_num = book_info.find('span', {'class': 'pl'}).get_text().strip()
                # 出版信息,作者
                pub = book_info.find('div', {'class': 'pub'}).get_text().strip()
                pub_list = pub.split('/')
                try:
                    author_info = '作者/译者: ' + '/'.join(pub_list[0:-3])
                except:
                    author_info = '作者/译者: 暂无'
                try:
                    pub_info = '出版信息: ' + '/'.join(pub_list[-3:-1])
                except:
                    pub_info = '出版信息: 暂无'
                try:
                    price_info = '价格: ' + '/'.join(pub_list[-1:])
                except:
                    price_info = '价格: 暂无'
                try:
                    rating_num= book_info.find('span', {'class': 'rating_nums'}).get_text().strip()
                except:
                    rating_num = '0.0'
                book_data = {
                    'title': title,
                    'people_num': people_num,
                    'author_info': author_info,
                    'pub_info': pub_info,
                    'price_info': price_info,
                    'rating_num': rating_num
                }
                # return book_data
                if book_data:
                    save_to_mongo(book_data,tag_name)
    except:
        print('解析错误')
        return None

到这里,我们已经可以获取到单个tag下单页的图书信息,这个时候我们只需要加入翻页功能就可以实现单个tag下所有图书的信息爬取。

点击下一页,分析页面请求 可以看到页面多了start和type两个参数,同时start参数是从0开始并以20的偏移量递增的,我们按照这个规律可以构建一个生成器以生成start参数。 从文章的第一张图,可以看出不同的tag页有不同的数量的图书,那页面数量也不尽相同,这时应该如何构建生成器? 这个时候我们发现所有的tag在第50页之后都请求不出信息了,所以我们只需构建前50页的页面链接即可,第51页显示如下:

第51页的显示结果

# 请求tag下的每个页面
def get_tag_page(tag_url,page):
        formdata = {
            'start': page,
            'type': 'T'
        }
        url = tag_url[0]+'?'+ urlencode(formdata)
        try:
            reponse = requests.get(url, headers=headers)
            if reponse.status_code == 200:
                return reponse.text
            return None
        except RequestException:
            print('请求列表页错误')
            return None

反反爬 豆瓣的反爬简单粗暴,直接封IP,为了爬虫的健壮,可以使用代理或者随机Header+随机时延的方式,随机时延可以设置为30到40之间,不过这样大大影响了爬取速率,如果需要快速爬取可以采用代理+多线程+随机Header+随机时延这样就能避过反爬又能快速爬取。

#使用Faker库随机生成虚假header
from faker import Faker
fake = Faker()
headers ={'User-Agent':fake.user_agent()}

留心

  • 写给之后的自己 文章是写完代码后,重新回顾的时候写的,回顾之后发现有很多需要优化的地方,比如异常处理部分经常考虑不周导致在爬取的时候异常中断,不得不重新排查错误。还有爬虫的断点续传的功能应该去了解学习下。

本文分享自微信公众号 - 咸鱼学Python(xianyuxuepython)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-06-03

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券