爬取某東600多本書籍,用數據幫你分析哪些Python書籍值得選擇!

源碼:私信小編001 即可

最近有好幾個讀者私下問我:剛接觸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:
"""
# bbook_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()

▲由於插入代碼之後格式亂,手動調整格式可能會出錯,最好是後台獲取源碼▲

整篇文章爬虫部分就已經完成了。接下來就是運行該文件,爬取書籍信息了。爬到的書籍一部分信息如下:

至此,這篇文章的上篇就分享到這裡,接下來會分享下篇,主要是通過對爬到的書籍進行分析,找出性價比更搞的書籍,幫助大家在選擇買書的時候可以多一份參考,少一分焦慮。


推薦閱讀:

TAG:Python | 數據分析 |