知识传送门:正则表达式
要想每天定时启动程序请看这篇文章
在学完莫烦B站的爬虫视频的3.3节之后,我完成了这个项目,感觉收获还是不小的。
体会是自己练手感觉进步挺快,能做出一个小项目给了我一些自信心,中间几个棘手的问题:
1如何获取下一页:分析我的主页的HTML,发现有一个总的文章数、有pagesize(每页最多显示几篇文章),有此两值就可以得到总的页数。再发现每一页的url是这样的https://blog.csdn.net/liuchengzimozigreat/article/list/2,每页链接基本一致,最后一个数字代表的是第几页,于是就可以用字符串拼接来得到所有分页的url;
2如何获取各个文章的信息:这主要去分析具体的HTML,看你需要的信息具体在哪儿,然后用解析器帮助你得到想要的HTML模块,然后具体再去每个模块中提取信息;
3我要存什么信息、怎么存:好吧,这个问题其实是我最后碰到的,前面程序做的差不多了,最后才想着我要怎么存、存什么,当然之前你可能想过一些,但如果想的不是很清楚的话,这个时候你就要停下来好好想想了。这个问题关系有点大,如果一开始就了然于心,就可以让我们思路更加顺畅;
4正则表达式的问题:正则表达式还是推荐这篇文章,我看完莫烦的正则表达式之后,其实立马就忘了好多,还是碰到具体问题之后,去看那篇文章中总结的表,于是从一开始啥也不会,到后面正则表达式越来越得心应手。
下面是我的代码,感觉写得很乱,以后还是多写一些函数才好,都写在主函数里,让人不想看了简直:
# Download amazing pictures from national geographic
from bs4 import BeautifulSoup
import requests
import re
import datetime
import pandas as pd
import os
# 提取第一页至最后一页的url
def get_allpages_url(page_info):
''''''
# 以上是script_lst[-9]的信息,有三个数:currentPage, pageSize, listTotal '\d'只查找数字一次,'\d+'则往后查找数字无限次
currentPage, pageSize, listTotal = [int(num) for num in re.findall(r'\d+', page_info)]
pageNum = listTotal//pageSize + 1 # 总的网页数
print('总的网页数:', pageNum, '总的文章数:', listTotal, 'pageSize:', pageSize)
baseUrl = re.findall(r'\'(.*)\'', page_info)[0] # .*在正则表达式中表示匹配除了'\n'之外的任何字符0次或无限次,在DOTALL中也可以匹配'\n',运行后发现这里就可以
allpages_url_lst = [baseUrl + '/' + str(page_num) for page_num in range(1, pageNum+1)] # 这里左闭右开,所以总的网页数要+1
return allpages_url_lst, listTotal
# 获取总体信息
def get_general_info(soup):
general_info = soup.find('div', {'class': 'data-info d-flex item-tiling'})
dl_info = general_info.find_all('dl')
# print(info)
self_article_num = dl_info[0]['title'] # 原创文章数量
fans_num = dl_info[1]['title'] # 粉丝数
like_num = dl_info[2]['title'] # 喜欢数
comment_num = dl_info[3]['title'] # 评论数
# print(self_article_num, fans_num, like_num, comment_num)
return self_article_num, fans_num, like_num, comment_num
# 获取等级信息
def get_grade_info(soup):
grade_info = soup.find('div', {'class': 'grade-box clearfix'})
# print(grade_info)
dd_info = grade_info.find_all('dd')
grade = dd_info[0].a['title'].split(r',')[0] # 等级
total_read_num = dd_info[1]['title'] # 总阅读量
earn_points = dd_info[2]['title'] # 积分
rank = grade_info.find_all('dl')[-1]['title'] # 排名
return grade, total_read_num, earn_points, rank
if __name__ == '__main__':
now_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
record_time = str(now_time) # 将作为数据存档时间来保存
date_today = datetime.datetime.now().date() # 今天的时间,将作为文章索引
# print(date_today, record_time)
URL = "https://blog.csdn.net/liuchengzimozigreat" # 我的blog主页(首页/第一页)
# find list of image holder
html = requests.get(URL).text
soup = BeautifulSoup(html, 'lxml')
# 提取总体信息
self_article_num, fans_num, like_num, comment_num = get_general_info(soup)
# 提取等级信息
grade, total_read_num, earn_points, rank = get_grade_info(soup)
# 存储总体信息
file_path = r'H:\learning like never feel tired\Scraping python\my_blog_info' # 存储位置
general_info_file = file_path + '\\AAA_general_info.csv' # 前面加三个A好让总体信息出现在文件夹第一的位置
if os.path.exists(general_info_file):
df = pd.read_csv(general_info_file, index_col=0, engine='python', encoding='utf_8_sig', parse_dates=[0])
else:
df = pd.DataFrame(columns=('self_article_num', 'fans_num', 'like_num', 'comment_num', 'grade', 'total_read_num', 'earn_points', 'rank', 'record_time'))
df.loc[date_today] = [self_article_num, fans_num, like_num, comment_num, grade, total_read_num, earn_points, rank, record_time] # 用当天日期作为索引,更新信息
df.to_csv(general_info_file, encoding='utf_8_sig')
# 提取各个文章的信息
script_lst = soup.find_all('script') # 提取其中的script标签tag,其中script_lst[-9]含有listtotal和pagesize信息
allpages_url_lst, total_article_num = get_allpages_url(str(script_lst[-9])) # 提取各page的url
page_article_lst = [] # 存储所有网页中article集合的信息
for page_url in allpages_url_lst:
html = requests.get(page_url).text
soup = BeautifulSoup(html, 'lxml')
article_info = soup.find_all('div', {'class': 'article-item-box csdn-tracking-statistics'})
page_article_lst.append(article_info)
# print(type(article_info))
article_info_lst = [] # 将所有文章整合进同一个列表中
for elem in page_article_lst:
mark = 0
for article in elem:
if mark == 0: # 查看HTML发现每一页中都会在最开头的地方多一篇未显示的文章'帝都的凛冬'
mark = 1
continue
article_info_lst.append(article)
# 提取文章属性并保存之,将保存八个信息:文章信息记录的日期——作为索引、文章名、文章类型、创建时间、阅读量、评论量、文章url、该条记录时间(精确到秒)
for article in article_info_lst:
# print(article)
span = article.find_all('span') # span包含了四个属性,依次是:article_type, date, read_num, comment_num
article_type = re.split(r'\s*', str(span[0]))[3] # 文章类型
create_date = span[1].get_text() # 创建时间
read_num = span[2].get_text() # 阅读数
comment_num = span[3].get_text() # 评论数
article_url = article.a['href'] # 文章链接
# 用'\n'分割结果类似:['', ' 原 ', ' linux命令学习汇总 '],需将最后一个字符串左右两边空格去掉才是我们想要的结果
# 用' {2,}'——空格2到无限次分割结果类似:['', 'rails官方指南--建一个简易博客', ''],因此去列表中的第二个作为我们的article_name
article_name = re.split(r' {2,}', re.split(r'\n*', str(article.find('a').get_text()))[-1])[1] # 文章名称 当时我还不知道,其实换成re.sub()更好
file_name = re.sub(r'[\s\':,\.()]*', '', article_name)
file = file_path + '\\' + re.findall(r'\w{2,20}', file_name)[0] + '.csv' # 文件名不宜太长,所以最多取20个\w——单词字符[A-Za-z0-9],过短又会冲突
if os.path.exists(file):
print('old article:', file)
df = pd.read_csv(file, parse_dates=[0], index_col=0, engine='python', encoding='utf_8_sig') # 有时难免程序在一天内多次运行,读入数据是保证一天只记录一个数据
else:
print('NEW ARTICLE:', article_name)
df = pd.DataFrame(columns=('article_name', 'article_type', 'create_date', 'read_num', 'comment_num', 'article_url', 'record_time'))
# print(date_today)
df.loc[date_today] = [article_name, article_type, create_date, read_num, comment_num, article_url, record_time] # 用当天日期作为索引,更新信息
df.to_csv(file, encoding='utf_8_sig')
# 下面是一些HTML目标模块的信息,也就这些信息,才能提炼出你想要的结果
'''
# 每个article的原始信息
转
postgresql数据库常用操作命令及SQL语言
2017-12-11 16:10:49
阅读数:914
评论数:0
'''
'''
# general_info的信息,包含原创、粉丝、喜欢、评论
'''
'''
'''