实现功能:爬取本人随手记账户中的记录,保存到word文档,包括:日期,分类,说的话和图片
具体步骤如下:
一、登陆账户
首先要访问随手记,需要登陆才能看到详细记录,之前考虑浏览器填写表单,POST给服务器,但是抓取了几次POST数据后,发现每次POST的密码都进行了某种加密,每次都不一样,如图所示:
没有做过这方面的研究,不知道如何POST,(期待大神指教!),只好作罢,这里利用COOKIE登陆。
def Login(cookie_str):
#浏览器登录后得到的cookie,一定要是Response Header里 status为200 OK的那个数据里的cookie
# cookie_str = cookie
#把cookie字符串处理成字典,以便接下来使用
cookies = {}
for line in cookie_str.split(';'):
key, value = line.split('=', 1)
cookies[key] = value
#设置请求头
headers = {'User-agent':'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0'}
#在发送get请求时带上请求头和cookies
#resp = requests.get(url, headers = headers, cookies = cookies)
#用get方法,得到的实际是https://www.sui.com/tally/new.do的结果,没有需要的数据
resp = requests.get(url,headers = headers, cookies = cookies)
#检查是否登录成功
if re.compile(r"个人中心").search(resp.text):
print('Login Success')
return (cookies,1)
else:
print('Login failed')
return (cookies,0)
这里需要注意的是:
1.并不是F12开发者工具截获的每个消息的COOKIE都可以用,必须选择Response Header里 status为200 OK的那个数据里的cookie
2.具体requests用get 方法还是post方法,需要针对获得的数据进行分析,看浏览器是执行了哪种操作,爬虫也执行相应的操作即可,这里判断是否登录成功,用get方法即可得到正确的结果,也可以利用status=200作为判断依据。
二、获取本月的记录总共几页
def getPageNum(cookies,beginDate,endDate):
headers = {'User-agent':'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0'}
payload = {'opt':'list2','beginDate':beginDate,'endDate':endDate,'cids':'0',
'bids':'0','sids':'0','pids':'0','memids':'0','order':'','isDesc':'0',
'page':1,'note':'','mids':'0'}
resp = requests.post(url,headers = headers, cookies = cookies, data = payload)
data = json.loads(resp.text)
#print(type(data)) 8个元素的dict
pageCount = data['pageCount']
return pageCount
这里的payload是从F12抓到的post的各个字段,每个都照抄下来,利用requests的post方法,即可抓取到需要的内容。
另外,注意到我们需要抓取的数据是在F12开发者工具的network里的XHR一项中,也就是动态加载得到的,那么我们得到的数据也需要json.load方法进行加载。然后在对加载后的数据进行分析,到底是多少个元素的列表还是字典之类的,直到解析到我们需要的数据。
三、具体爬取
def spider(doc,cookies,beginDate,endDate,pageNum):
headers = {'User-agent':'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0'}
for page in range(1,pageNum+1):
payload = {'opt':'list2','beginDate':beginDate,'endDate':endDate,'cids':'0',
'bids':'0','sids':'0','pids':'0','memids':'0','order':'','isDesc':'0',
'page':page,'note':'','mids':'0'}
resp = requests.post(url,headers = headers, cookies = cookies, data = payload)
# print(resp.text)
data = json.loads(resp.text)
#print(type(data)) 8个元素的dict
groups = data['groups']#['list']
#print(type(groups)) 10个元素的list,每个元素是一个dict
for n in range(0,len(groups)):#遍历多条记录
group = groups[n]
item_list = group['list'] # 具体内容都在这个list下,类型:列表中具体几个元素,要看记录了几条
for m in range(0,len(item_list)):
item = item_list[m] # item 包含各个标签下的数据,为一个字典
date_dic = item['date']
year = date_dic['year'] + 1900
month = date_dic['month'] + 1
day = date_dic['date']
categoryName = item['categoryName']
memo = item['memo']
image_url = item['url']
date = '{}年{}月{}日'.format(str(year),str(month),str(day))
print(date)
# print(categoryName,memo,image_url)
if m == 0: #日期标题只输出一次
heading = doc.add_heading()
title = heading.add_run(date)
title.font.bold = True
table = doc.add_table(rows= 1,cols=2)
cells = table.rows[0].cells
cells[0].text = categoryName
cells[1].text = memo
paragraph = doc.add_paragraph(image_url)
# table.cell(m,2).text = image_url
if image_url and re.compile(r"http").search(image_url):
response = requests.get(image_url)
img = response.content
binary_img = BytesIO(img)
doc.add_picture(binary_img, width=Inches(2))
doc.save('D:/Code/python/Suispider/secret.doc')
1.本部分程序,将抓取到的记录保存到word文档中,传递给本函数的就有名称为doc的实例,后续将抓取到的结果保存到文档中,这里doc部分采用了python-docs包,包括添加标题add_heading, 添加表格 add_table,添加段落add_paragraph,等,具体用法可以参考https://python-docx.readthedocs.io/en/latest/。安装后调用的时候我记得报错了,后来安装对应的whl文件,才解决
2.另外,对于抓到的图片网址,像保存到word中,需要BytesIO方法将Bytes类型的图片数据直接保存到word,而不用下载到本地再弄到word中,这里参考了https://stackoverflow.com/questions/24341589/python-docx-add-picture-from-the-web
四、总的代码
# -*- coding: utf-8 -*-
"""
Created on Thu Nov 22 16:25:49 2018
创建爬取随手记记录的爬虫
@author: Jzz
"""
import requests
import sys
from bs4 import BeautifulSoup
import re
import json
import calendar #为了确定终止时间是在某个月的几号
from docx import Document
from docx.shared import Inches
import io
import time
def Login(cookie_str):
#浏览器登录后得到的cookie,一定要是Response Header里 status为200 OK的那个数据里的cookie
# cookie_str = cookie
#把cookie字符串处理成字典,以便接下来使用
cookies = {}
for line in cookie_str.split(';'):
key, value = line.split('=', 1)
cookies[key] = value
#设置请求头
#headers = {'User-agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36'}
headers = {'User-agent':'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0'}
#在发送get请求时带上请求头和cookies
#resp = requests.get(url, headers = headers, cookies = cookies)
#用get方法,得到的实际是https://www.sui.com/tally/new.do的结果,没有需要的数据
resp = requests.get(url,headers = headers, cookies = cookies)
#检查是否登录成功
if re.compile(r"个人中心").search(resp.text):
print('Login Success')
return (cookies,1)
else:
print('Login failed')
return (cookies,0)
def getPageNum(cookies,beginDate,endDate):
headers = {'User-agent':'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0'}
payload = {'opt':'list2','beginDate':beginDate,'endDate':endDate,'cids':'0',
'bids':'0','sids':'0','pids':'0','memids':'0','order':'','isDesc':'0',
'page':1,'note':'','mids':'0'}
resp = requests.post(url,headers = headers, cookies = cookies, data = payload)
data = json.loads(resp.text)
#print(type(data)) 8个元素的dict
pageCount = data['pageCount']
return pageCount
def spider(doc,cookies,beginDate,endDate,pageNum):
headers = {'User-agent':'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0'}
for page in range(1,pageNum+1):
payload = {'opt':'list2','beginDate':beginDate,'endDate':endDate,'cids':'0',
'bids':'0','sids':'0','pids':'0','memids':'0','order':'','isDesc':'0',
'page':page,'note':'','mids':'0'}
resp = requests.post(url,headers = headers, cookies = cookies, data = payload)
# print(resp.text)
data = json.loads(resp.text)
#print(type(data)) 8个元素的dict
groups = data['groups']#['list']
#print(type(groups)) 10个元素的list,每个元素是一个dict
for n in range(0,len(groups)):#遍历多条记录
group = groups[n]
item_list = group['list'] # 具体内容都在这个list下,类型:列表中具体几个元素,要看记录了几条
for m in range(0,len(item_list)):
item = item_list[m] # item 包含各个标签下的数据,为一个字典
date_dic = item['date']
year = date_dic['year'] + 1900
month = date_dic['month'] + 1
day = date_dic['date']
categoryName = item['categoryName']
memo = item['memo']
image_url = item['url']
date = '{}年{}月{}日'.format(str(year),str(month),str(day))
print(date)
# print(categoryName,memo,image_url)
if m == 0: #日期标题只输出一次
heading = doc.add_heading()
title = heading.add_run(date)
title.font.bold = True
table = doc.add_table(rows= 1,cols=2)
cells = table.rows[0].cells
cells[0].text = categoryName
cells[1].text = memo
paragraph = doc.add_paragraph(image_url)
# table.cell(m,2).text = image_url
if image_url and re.compile(r"http").search(image_url):
response = requests.get(image_url)
img = response.content
binary_img = BytesIO(img)
doc.add_picture(binary_img, width=Inches(2))
doc.save('D:/Code/python/Suispider/secret.doc')
#登录后才能访问的网页
url = 'https://www.sui.com/tally/new.rmi'
# 遍历不同账本,需要用不同的cookie即可实现,没有多余post参数
cookie_str = r'输入自己的cookie'
login_result = Login(cookie_str)
cookies = login_result[0]
set_year = 2018
doc = Document()
if login_result[1] == 1:
for month in range(11,10,-1):
# month = 11
last_day = calendar.monthrange(set_year,month)[1] #确定最后一天是几号
beginDate = '{}.{}.01'.format(str(set_year),str(month))
endDate = '{}.{}.{}'.format(str(set_year),str(month),str(last_day))
page_num = getPageNum(cookies,beginDate,endDate) #获取这个月记录了几页
spider(doc,cookies,beginDate,endDate,page_num)
time.sleep(5)
另外,如果有多个账本,想都爬取下来,只用使用不同的cookie即可,之前考虑是POST了什么参数,但是没有找到,只能用这种办法。
欢迎多多批评指正!