部分网站的数据需要用户登录后才能查看,因此爬虫程序需要模拟用户进行登录操作才能获取到数据。这次需要熟悉两种常见的登录模式:基于Session与Cookie的登录,基于JWT登录。同时掌握使用MySQL数据库基本操作,来持久化爬取的数据。
1、安装Mysql和相应的python库:pymysql
2、为Python安装selenium、pyquery库,安装Chrome和对应ChromeDriver(见实验一)
爬取网站1:https://login2.scrape.center/
爬取网站2:https://login3.scrape.center/login
使用浏览器开发者工具(F12),分析网站登录请求,在登录后再分析获取数据的请求。根两种登录模式,写出相应的相应登录请求,获取数据并持久化到MySQL数据库中。基于Session与Cookie的登录代码已在资料给出。
实现基于JWT登录模式,实现对爬取网站2数据的爬取,并把数据持久化到MySQL,存储的表名为:spider_books,字段名称自定义,存储的字段信息包含:书名、作者、封面图像本地路径、评分、简介、标签、定价、出版社、出版时间、页数、ISBM
请把封面图像保存到本地文件夹中,因此封面图像本地路径为封面图像保存到本地的路径,而不是原始URL链接。
在完成基本要求的基础上,选项一:实现对当当网的新书上架内容数据(https://login2.scrape.center/)爬取(红框内的所有数据):
书籍信息需要保存到MySQL中,表名和字段名自定义,注意红框内为一个Tab展示列表,实际内容有4页,而不是8只有本书,应该有4*8本书。选项二:分析给出豆瓣、淘宝等网站是如何实现登录请求的,以及登陆后再次请求需要携带哪些信息,给出一个案例分析即可,不需要代码实现,必须给出分析思路和图片说明。
在完成基本要求的基础上,实现一个常见网站模拟登录代码,并输出需要登录访问的信息,无需持久化,输出至控制台即可,必须给出分析思路和图片说明。
模拟登录爬取网页内容,首先分析网站登录请求,在登录后再分析获取数据的请求。
如下图所示:
Authorization主要用作http协议的认证。
1. 构造JWT获取网站的一级页面,并把每次数据请求加入相应的token即可
目的:找到每本书的书号
得到结果如下图所示:
2. 通过书号获取书的详细信息的JSON数据
得到结果如下图所示:
总结:因为书籍信息部分缺失,所以很多用None代替了,后续会把这些数据特殊处理
3. 现在已经得到了相关数据信息,只需保存即可,把封面图像保存到本地文件夹中
4. 将爬下来的数据进行特殊处理并存储到变量中,None用空代替,这里只展示了书名与作者,其他数据都类似,都是用的三目运算符
5. 最后将数据保存到mysql数据库中即可
6. 数据库数据展示如下:
总结: 有些字段本来可以用int或float的,但是由于该字段有些缺失数据,所以所有字段都用字符串,把缺失字段都用空来替代。
7. 源码如下:
# coding=UTF-8
import pymysql
import requests
from urllib.parse import urljoin
# 存储封面图像本地路径
# LOCAL_PATH = "/usr/local/images/"
LOCAL_PATH = "D:\images\\"
BASE_URL = 'https://login3.scrape.center/'
LOGIN_URL = urljoin(BASE_URL, '/api/login')
BOOK_URL = urljoin(BASE_URL, '/api/book/')
USERNAME = 'admin'
PASSWORD = 'admin'
session = requests.Session()
"""
requests库的session会话对象可以跨请求保持某些参数。
说白了,就是比如你使用session成功的登录了某个网站,
则再次使用该session对象对该网站的其他网页访问时都会默认使用该session之前使用的cookie等参数。
"""
# Authorization主要用作http协议的认证。
# 构造JWT获取网站的一级页面,并把每次数据请求加入相应的token即可
# 获取网站的一级页面 目的:找到每本书的书号
def get_profile():
jwt_token = session.post(LOGIN_URL, data={
'username': USERNAME,
'password': PASSWORD
}).json()['token']
res = requests.get(BOOK_URL, headers={"Authorization": f"jwt {jwt_token}"}).json()['results']
return res
# 通过书号获取书的详细信息
def get_details(book_id):
jwt_token = session.post(LOGIN_URL, data={
'username': USERNAME,
'password': PASSWORD
}).json()['token']
res = requests.get(BOOK_URL + book_id, headers={"Authorization": f"jwt {jwt_token}"}).json()
return res
# 将爬下来的数据进行处理并存储到变量中
def get_info(id):
global name, authors, local_path, score, intro, tags, price, publisher, published_at, page_number, isbn
detail = get_details(id)
name = detail['name'] if detail['name'] is not None else ""
authors = " ".join(map(lambda x: x.strip().replace(" ", ""), detail['authors'])) \
if detail['authors'] is not None else ""
cover = detail['cover'] if detail['cover'] is not None else "None"
local_path = download_cover(LOCAL_PATH, id, cover)
score = eval(detail['score']) if detail['score'] is not None else ""
intro = detail['introduction'][:255] if detail['introduction'] is not None else ""
tags = " ".join(detail['tags']) if detail['tags'] is not None else ""
price = detail['price'] if detail['price'] is not None else ""
publisher = detail['publisher'] if detail['publisher'] is not None else ""
published_at = detail['published_at'] if detail['published_at'] is not None else ""
page_number = int(detail['page_number']) if detail['page_number'] is not None else ""
isbn = detail['isbn'] if detail['isbn'] is not None else ""
# 以书号为名,下载封面图片
def download_cover(route, book_num, url):
if url == "None":
return ""
else:
with open(route + book_num + ".jpg", 'wb') as fb:
fb.write(requests.get(url).content) # content返回的是bytes,二进制数据
return route + book_num + ".jpg"
# 将数据持久化到MySQL数据库中
if __name__ == '__main__':
infos = get_profile()
# 创建数据库spiders connect:连接
mydb = pymysql.connect(host='localhost', user='root', password='123456', port=3306)
mycursor = mydb.cursor() # 用来获得python执行Mysql命令的方法 cursor:游标 指针
mycursor.execute("CREATE DATABASE IF NOT EXISTS spiders") # execute:执行
# 创建表spider_books
mydb = pymysql.connect(host='localhost', user='root', password='123456', port=3306, db='spiders')
mycursor = mydb.cursor()
mycursor.execute("CREATE TABLE IF NOT EXISTS spider_books ("
"id INT(10) PRIMARY KEY COMMENT'书号',"
"name VARCHAR(255) COMMENT'书名',"
"authors VARCHAR(255) COMMENT'作者', "
"local_path VARCHAR(255) COMMENT'封面图像本地路径',"
"score VARCHAR (10) COMMENT'评分',"
"intro VARCHAR(255) COMMENT'简介',"
"tags VARCHAR(255) COMMENT'标签',"
"price VARCHAR(10) COMMENT'定价',"
"publisher VARCHAR(50) COMMENT'出版社',"
"published_at VARCHAR(50) COMMENT'出版时间',"
"page_number VARCHAR (5) COMMENT'页数',"
"ISBM VARCHAR(255) COMMENT'ISBM')")
# 插入数据
sql = 'INSERT INTO spider_books values(%s,%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)'
# 只取前10条数据
for info in infos[0:10]:
book_id = info['id']
get_info(book_id)
try:
mycursor.execute(sql,
(book_id, name, authors, local_path, score, intro, tags, price, publisher, published_at,
page_number, isbn))
mydb.commit()
print('Insert successfully')
except Exception as err:
mydb.rollback() # 数据回滚
print("Failed To Insert")
print(err)
mydb.close()
此处实现的是选项一:实现对当当网的新书上架内容数据爬取,话不多说,分析网页数据,如图所示: