点击“小詹学Python”,选择“置顶”公众号

重磅干货,第一时间送达

本文转载自Python全家桶,禁二次转载

最近有好几个读者私下问我:刚接触Python、或打算要学习Python,不知道选什么书比较合适,当时只根据自己的Python经验和学习感受,给读者推荐了一些自认为不错的。但是,毕竟一个人接触少,局限性太大,也许还有更多、更好的好书只是我没有接触过。于是就打算实际操作,通过爬虫方式爬取某东上的书籍、通过数据来帮助大家更科学、更合理的选择学习资料。

本文概要

说真的,在互联网爆发的今天,想要找一本Python书那真的太简单了,去某东或某宝,随便敲一个Python,各种各样的书籍扑面而来。好的是可选择性多了,坏的是面对这些层次不齐的书,到底该选择那一本,于是就有了这篇文章。

本篇文章分为上、下两篇,今天是上篇,主要分享如何爬取书籍信息


  • 上篇主要是分享如何通过Python爬取某东上的书籍信息

  • 下篇主要是通过对爬取的数据进行分析,帮大家寻找一些口碑和销量都不错的书籍。

再看看需要爬取的书籍指标:


  • 书名:买书必须要看的参数

  • 价格:价格毋庸置疑,是必须要考虑的参数

  • 评论数:侧面反映书的好坏。某东的书籍无销量,可以通过评论数来反映销量

  • 好评率:最直观的指标,能够反应读者使用的体验

  • 排名:某东自营书籍有的会有销量榜排名,所以也是非常重要的参数

  • 是否自营:自营书籍会有书籍的排名,并且物流有保证

环境搭建

在开始爬虫之前,先要搭建一个爬虫的虚拟环境。我是在window10系统,使用anconda进行环境管理,大家可以根据自己的系统和操作习惯自行选择。

  • 爬虫环境:Windows10 + Anconda + Python3.7 + Request

  • 本文虚拟环境:python_books_env

    需要安装的包:request、matplotlib、pandas、fake_useragent、lxml

页面分析

再明确了本文要爬取得参数之后,接下来爬虫才算正式开始。首先,我们根据当前的需求,去某东商城找到对应的请求初始URL。本文主要是爬取python书籍,所以直接打开某东商城,在搜索栏直接输入:python,就会出现我们需要的Python书籍:

1. 翻页分析

这个只是搜索页界面的url,当你在点击页面下面的第一页时,你会发现第一页面的url会反生变化,然后在点击第二页、第三页的时候,url的如下:

# 前三页的url
https://search.jd.com/Search?keyword=python&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&wq=python&page=1&s=1&click=0
https://search.jd.com/Search?keyword=python&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&wq=python&page=3&s=57&click=0
https://search.jd.com/Search?keyword=python&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&wq=python&page=5&s=117&click=0

对比这三个url,发现page、s这两个参数随着每次的翻页,会发生变化。但是作为一个上过高中、学过等差数列的我,

一下子就找到了翻页规律:2n-1

但是问题又来了,s是个什么东西,大大的三个问号在我心里。不过不要急,当试着去掉这个s参数时,界面居然没有发生变化,这个参数是用来搞笑的吗?不过对于爬虫来说,这种对数据无影响的参数直接干掉就是了(click这个参数也对数据没影响,直接干掉)。

于是,通用的请求的url如下:

https://search.jd.com/Search?keyword=python&
enc=utf-8&qrst=1&rt=1&stop=1&vt=2&wq=python
&page=2n-1

2. 评论数和好评率

上边搞定翻页之后,接下来就是对每一本书参数进行提取了。右键查看源码,随机看一本书,书名、价格、详情页url、是否自营这4个参数直接在当前页面响应中就可以提取的到,但发现评论数这个便签显示

是空的,某有错。

▲查看源码评论截图▲

不过不要慌,问题不大。经分析评论相关的数据是通过Ajax请求动态加载的,对于这种情况,直接右键选择检查, 通过NetWork抓包分析,很快就找到了评论的请求路径:

不过评论的url里面包含referenceIds、_这两个参数。对于抓包的url里面referenceIds是所有书籍的sku_id,返回的是当前页所有书的评论信息。而我在抓取数据的时候,为了保证评论数正确性和代码的可读性,请求只携带当前书籍sku_id,返回当前书籍的评论数和好评率。_参数是时间戳,很好处理。

轻轻松松搞定了评论

所以,通用的评论请求的url如下:


"https://sclub.jd.com/comment/productCommentSummaries.action?referenceIds={}&callback=jQuery5954857&_={}"

3. 详情页获取销量排名

自营书的销量排名数据是在详情页面,所以这次需要进入详情页去一探究竟。还是刚才的操作,先看了一波源码,毛都没看到。不过结合之前评论的操作,继续右键检查,经过分析,排名参数也是通过Ajax请求动态加载的,

嗖嗖的就搞定了

经过分析和验证,只需要将找到的url里面参数skuId换成动态的就行。别的参数不用再做处理。

到这里,最后一个参数也分析完毕,通用url如下:

https://c.3.cn/book?skuId={}&cat=1713,3287,3797&area=1_72_2799_0
&callback=book_jsonp_callback

Show Code

越往后书籍,书的销量和质量都在下降,所以这里只爬取前20页的。经过上边的分析,整个爬虫的页面分析已经逼逼完成,接下来就是代码的展示。

好的,不逼逼了,直接操家伙,上代码:

import requests
from lxml import etree
from userAgent import USER_AGENT_LIST
import random
import time
import re
import os
import csv
from fake_useragent import UserAgent

# 实例化一个ua对象
ua = UserAgent()

class PythonBookSpider(object):
    """爬取京东商城前20页的Python书籍"""
    def __init__(self):
        self.base = "https://search.jd.com/Search?keyword=python&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&wq=python&page={}"
        self.comment_url = "https://sclub.jd.com/comment/productCommentSummaries.action?referenceIds={}&"
                     "callback=jQuery5954857&_={}"
        self.rank_url = "https://c.3.cn/book?skuId={}&cat=1713,3287,3797&area=1_72_2799_0&callback=book_jsonp_callback"
        self.headers = {
              "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) "
              "Chrome/22.0.1207.1 Safari/537.1",
              "authority": "search.jd.com"
             }

def _send_request(self, url):
        """
        发送请求,获取响应
        :param url: 请求路径
        :return:
        """
        # self.headers["User-Agent"] = random.choice(USER_AGENT_LIST)
        self.headers["User-Agent"] = ua.random
        time.sleep(0.5)
        response = requests.get(url=url, headers=self.headers, timeout=5)
        return response

def send_request(self, url):
        """主要是对请求响应进行处理"""
        try:
            response = self._send_request(url)
        except Exception as e:
            print("request error: {}".format(e))
            return None
        if response.status_code != 200:
            content = None
        else:
           content = response.content
        return content

def get_comment_count(self, sku_id):
        """获取评论数"""
        print("comment url: {}".format(self.comment_url.format(sku_id, int(time.time()))))
        response = self.send_request(self.comment_url.format(sku_id, int(time.time())))
        if not response:
            return "", ""
        # 响应的编码方式可以在响应中查看
        response = response.decode("GBK")
        good_rate = re.findall(""GoodRate":(d.d+)", response)[0] if re.findall(""GoodRate":(d.d+)",
                 response) else ""
        commet_count = re.findall(""CommentCount":(d+)", response)[0] if re.findall(""CommentCount":(d+)",
                 response) else ""
        # print(" good rate: {}".format(good_rate))
        # print(" comment count: {}".format(commet_count))
        return good_rate, commet_count

def parse_book_rank(self, sku_id):
        """
        获取京东自营书籍销量排行榜名次
        :param sku_id: int 书籍的sku_id
        :return:
        """
        # b'book_jsonp_callback({"yn":2,"rank":86,"ebookId":0})'
        response = self.send_request(self.rank_url.format(sku_id))
        if not response:
            return False, None
        print("b_rank:{}".format(response.decode()))
        b_rank = re.findall(r""rank":[-|0-9][0-9]*", response.decode())
        b_rank = b_rank[0].split(":")[1] if b_rank else ""
        return True, b_rank

def save_book_info(self, books_list):
        """
        保存书籍信息
        :param files: 每一页的书籍信息
        """
        if not os.path.exists("./PythonBookInfo.csv"):
            with open("./PythonBookInfo.csv", "a+", newline="") as file:
                writer = csv.writer(file)
                writer.writerow(["name", "price", "is_self_operated", "comment_counts", "good_comments_rate",
                     "good_comments_rate", "sale_rank"])
        with open("./PythonBookInfo.csv", "a+", newline="") as file:
            writer = csv.writer(file)
            for book_list in books_list:
                try:
                    writer.writerows(book_list)
                except:
                    continue

def parse_index_page(self, response):
        """
        解析首页的界面信息
        :param response: type: str 搜索页面的响应
        :return: type: list 每一页的全部书籍相关信息
        """
        index_selector = etree.HTML(response)
        books_list = index_selector.xpath('//div[@id="J_goodsList"]/ul/li') # 解析每一页的书籍列表
        py_bookinfo_list = []
        for book in books_list:
            # 书籍详情地址
            b_url = book.xpath('.//div[@class="p-img"]/a/@href')
            # 图书价格
            b_price = book.xpath('.//div[@class="p-price"]//i/text()')
            # 卖家方式
            b_seller = book.xpath('//div[@class="p-icons"]/i[1]/text()')
            # 书名称
            b_name = book.xpath('.//div[@class="p-name"]//em')
            # print("b_name: {}".format(b_name[0].xpath("string(.)")))
            b_name = [] if not b_name else b_name[0].xpath("string(.)").strip()
            # 书的评论数:通过js加载的
            sku_id = book.xpath('./@data-sku')[0]
            if not sku_id:
                continue
            good_rate, commet_count = self.get_comment_count(sku_id)
            if not all([b_url, b_price, good_rate, commet_count, b_name]):
                continue
            detail_url = "https:" + b_url[0] if not b_url[0].startswith("https") else b_url[0]
            # print("detail url:{}".format(detail_url))
            # 如果是京东自营的话,在抓取对应的自营排名、出版社
            if b_seller[0] == "自营":
                # 获取书籍销售排行榜名次
                rank_response = self.parse_book_rank(sku_id)
                if not rank_response:
                    continue
                b_rank = rank_response[1]
                b_seller = b_seller[0]
                # 获取书籍出版社
                # b_publisher = self.parse_detail_page(detail_url)
            else:
                b_rank = ""
                b_seller = ""
            py_bookinfo_list.append([[b_name, b_price[0], b_seller, commet_count, good_rate, b_rank, detail_url]])
      return py_bookinfo_list

def spider(self):
        """spider的主要逻辑业务"""
        for page in range(1, 21):
            # 1.请求搜索页,获取书籍列表页面信息,这里请求前20页
            first_response = self.send_request(self.base.format(2 * page - 1))
            if not first_response:
               continue
            # 2.解析搜索页书籍的相关信息
            py_bookinfo_list = self.parse_index_page(first_response)
            if not py_bookinfo_list:
                continue
            # 3.保存爬取书籍信息
            self.save_book_info(py_bookinfo_list)
            print("第 {}页爬取完成".format(page))

print("抬头 望天")

if __name__ == '__main__':
    py_spider = PythonBookSpider()
    py_spider.spider()

整篇文章爬虫部分就已经完成了。接下来就是运行该文件,爬取书籍信息了。爬到的书籍一部分信息如下:

至此,这篇文章的上篇就分享到这里,接下来会分享下篇,主要是通过对爬到的书籍进行分析,找出性价比更搞的书籍,帮助大家在选择买书的时候可以多一份参考,少一分焦虑。

爬取某东600多本书籍,用数据帮你分析哪些Python书籍值得选择(上)相关推荐

  1. 使用selenium爬取某东的手机商品信息

    程序完整代码 from selenium import webdriver # 自动化爬取工具库 import time # 让程序休眠一段时间的库 from lxml import etree # ...

  2. 如何利用 C# 爬取「猫眼电影专业版:票房」数据!

    在现代生活中,看电影已经成为大家的一种休闲方式. 前几天,我们介绍了 如何利用 C# 爬取「猫眼电影:热映口碑榜」及对应影片信息!,通过这份"热映口碑"榜单,我们可以看到大家对当前 ...

  3. Python爬取近10万条程序员招聘数据,告诉你哪类人才和技能最受热捧!

    来源:凹凸数据 本文约5800字,建议阅读15分钟 本文带你了解当下企业究竟需要招聘什么样的人才?需要什么样的技能? 随着科技的飞速发展,数据呈现爆发式的增长,任何人都摆脱不了与数据打交道,社会对于& ...

  4. 练习:selenium 爬取京东的电脑商品100页的数据并保存到csv文件中

    练习:selenium 爬取京东的电脑商品100页的数据并保存到csv文件中 from selenium.webdriver import Chrome, ChromeOptions import t ...

  5. Python爬取网上车市[http://www.cheshi.com/]的数据

    #coding:utf8 #爬取网上车市[http://www.cheshi.com/]的数据 import requests, json, time, re, os, sys, time,urlli ...

  6. 爬取网易财经中股票的历史交易数据

    爬取网易财经中股票的历史交易数据 需求分析 得到股票代码 股票代码的信息是在东方财富网中获取(http://quote.eastmoney.com/stocklist.html) 得到股票的历史交易记 ...

  7. 爬取全国未来十五天的天气数据(python3)

    爬取全国未来十五天的天气数据 一.网页分析 (一)请求分析(url) 1.观察 2.分析 3.结论 (二)行政区域分析 1.观察 2.分析 3.结论 (三)未来15日 天气数据分析 1.观察 2.分析 ...

  8. 爬取新浪微博新闻,包括模拟登陆,数据存储等!最适合新手的教程

    写在最开头 该程序主要是为爬取新浪微博,想要搜索的信息,主要报错的信息为文本,其他元素未涉及,此外微博博主信息,笔者也不关注,时间等信息同样不关注,主要目的就是获取文本信息.因此,本着对读者同样同样也 ...

  9. 爬虫实战(二)—利用requests、selenium爬取王者官网、王者营地APP数据及pymongo详解

    概述 可关注微信订阅号 loak 查看实际效果. 代码已托管github,地址为:https://github.com/luozhengszj/LOLGokSpider ,包括了项目的所有代码. 本文 ...

最新文章

  1. python twisted教程_Python Twisted系列教程16:Twisted 进程守护
  2. linux常用快捷命令(不断更新)
  3. PLSQL_性能优化系列20_Oracle Result Cash结果缓存
  4. Django的jinja2语法遇到jquery问题: defaultaddress is not defined
  5. 朴素贝叶斯算法注意事项(有待完善)
  6. apache缺省banner_http服务器缺省banner
  7. 吴裕雄--天生自然 高等数学学习:平面及其方程
  8. Java实训项目:GUI学生信息管理系统(2019)【下】
  9. C# async await
  10. java的诞生詹姆斯·高斯林
  11. DC-DC隔离电源模块与非隔离电源模块有什么区别?
  12. 基于龙芯CPU中标麒麟操作系统的国产半实物仿真系统ETestDEV
  13. 单引号在c语言中作用,我想知道单引号在C语言的具体作用
  14. 群晖docker中安装VS code
  15. 婴儿监护物联网系统设计系统源码开放
  16. 移动端电影院社交来啦 约上ta看一场电影
  17. 证券接口通达信系统对做趋势套利有何风险?
  18. altera fpga 型号说明_ALTERA的FPGA命名规则
  19. win10下载jdk18以及环境配置
  20. 数据集成平台,多种数据源融合

热门文章

  1. Linux下socket最大连接数 ulimit -n 最大值修改
  2. 反思深度思考:不无脑的行动 有脑的持久行动
  3. Linux的apache的allowoverwrite参数的解释
  4. 第1章 计算机基础知识习题答案,职称计算机基础知识习题第一章
  5. 化工原理少学时答案解析_化工原理 少学时 思考题答案
  6. laravel+php+支付功能,laravel+微信支付源码
  7. 湖南大学计算机考研考什么,2017年湖南大学计算机系统考研大纲
  8. 测试工具_10款优秀的浏览器兼容性测试工具
  9. php异步检测用户名是否已经存在,AJAX实例-检测用户名是否存在
  10. jq点击按钮获取php的值删除,通过jquery怎么移除点击事件