使用爬虫进行一次 hexo 构建的博客爬取并且生成 md 文档
来源:互联网 发布:淘宝cos店 三 编辑:程序博客网 时间:2024/05/16 08:49
起因
由于以前的博客文章在电脑重装的时候全没了,直接 cv 战士难免太过麻烦,正好好久没有写 python 了,于是决定写一个爬虫来爬取文章并且生成 md 文档
分析
使用的技术和库
这里使用 python + BeautifulSoup4(网页装载与解析) + urllib(发起请求) + codecs(写入文件)
主页
我们来看看主页,一篇文章的位置
再来看看所有文章是怎么分布的
这简直就是最简单的 list 结构嘛
分页
文章不可能就只有一页,所以对分页的研究就体现在分页的 url 上,这样我们就能狗一次爬到底
看看第二页的url
推断一下,第 6 页应该是 http://wintersmilesb101.online/page/6
果然没错
那么看看第一页是否可以写成 http://wintersmilesb101.online/page/1 呢?
说明首页需要特殊处理,即 http://wintersmilesb101.online
抓取页面大小
页面大小的 dom 结构如下
可以看到,这几个 页面 index 的 class 是一致的,所以我们需要通过 BeautifulSoup 来选中上一个元素(这里可以看出上一个是这个结构中唯一的),或者是通过 BeautifulSoup 的 select 方法选中 class = page-number 的元素列表,最后一个即为 pageSize 的元素
文章信息
我们需要哪些文章信息?
由于我们这里是使用的 hexo 来构建的博客,所以要按照他的规则来,一般来说我们需要如下结构
---title: Python3.7 爬虫(二)使用 Urllib2 与 BeautifulSoup 抓取解析网页date: 2017-04-08date: 2017-04-09categories: - 爬虫- Python 爬虫tags: - Python3- 爬虫- Urllib2- BeautifulSoup4---
这些信息,除了 标签,我们都可以在 文章列表页面就获取到了,如下:
当然这些信息,正文也页都有,正文页的链接,我们可以在 title 的位置获取到,与网站基础 url 拼接就可以获取到最终链接,不过有些 url 中有中文,因此我们需要使用 urllib.request.quote(link) 来把链接中的中文编码成 url 中的正确编码,这里会把 : 也转码了,转换成 %3A 因此,转换之后,我们还需要还原 %3A 为 :
正文的转换
正文就直接通过 获取到 class = post-body的元素,然后遍历子元素(通过 children 属性,注意 type 为 bs4.element.NavigableString 的元素,是无效元素,需要跳过),然后根据,html 与 markdown 的对应关系来转换成对应的 markdown 写法,不过在 BeautifulSoup 中还是有不少的坑点,这里代码中注释写的很清楚,就不赘述了
实现
import urllib.requestfrom bs4 import BeautifulSoupimport bs4import reimport xlwtimport osimport codecsfilePath = r"H:/GIT/Blog/WinterSmileSB101/source/_posts/old/"url = "http://wintersmilesb101.online"user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'req = urllib.request.Request(url, headers={ 'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'})print('发送 页面网络请求')response = urllib.request.urlopen(req)content = response.read().decode('utf-8')# output content of page#print(content)soup = BeautifulSoup(content, "lxml")# 获取页面数量spans = soup.select('span.space')pageHref = spans[spans.__len__()-1].nextSibling['href']# get total numpageNum = int(pageHref.split('/')[2])print(pageNum)# get other pageurlBase = "http://wintersmilesb101.online/page/"index = 1while index <= pageNum: # 索引大于 1 的时候需要重新指定 url if index > 1: url = urlBase+str(index) print(url) req = urllib.request.Request(url, headers={ 'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)' }) print('发送 页面网络请求 : '+url) response = urllib.request.urlopen(req) content = response.read().decode('utf-8') soup = BeautifulSoup(content, "lxml") # 获取文章 list articles = soup.find_all('article') # 处理每篇文章 for article in articles: # 获取创建时间 createTime = article.find('time', title="创建于").text.strip() # 获取创建时间 updateTime = article.find('time', title="更新于").text.strip() # 获取分类 categoies = article.find_all('a', attrs = {'itemprop': "url", 'rel': "index"}) # 分类的 url,Name categoryUrl = '' categoryName = '' for category in categoies: #print(category) categoryUrl += category['href']+',' #print(categoryUrl) categoryName += category.text.strip()+',' #print(categoryName) categoryUrl = categoryUrl[0:categoryUrl.__len__()-1] categoryName = categoryName[0:categoryName.__len__() - 1] # 获取正文 urlMain = '' link = article.link['href'] articleTitle = link.split('/')[link.split('/').__len__()-2] # print(articleTitle) # 转换中文 url 编码 urlMain = urllib.request.quote(link) # 把多余的转换 : ==> %3A ,还原 urlMain = urlMain.replace('%3A', ':') # print(urlMain) print('发送 文章网络请求') req = urllib.request.Request(urlMain, headers={ 'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)' }) response = urllib.request.urlopen(req) mainContent = response.read().decode('utf-8') # output content of page # print(mainContent) mainSoup = BeautifulSoup(mainContent,'lxml') body = mainSoup.find('div', itemprop="articleBody") blockquote = body.blockquote if blockquote != None: blockquoteText = blockquote.p.text # print(blockquote.p) extenalUrl = None mineUrl = blockquote.p.a['href'] if blockquote.p.find('a', rel="external"): extenalUrl = blockquote.p.find('a', rel="external")['href'] # print(extenalUrl) # 把其中的链接替换为 md 语法 if extenalUrl: blockquoteText = blockquoteText.replace("原文地址", "[原文地址](" + extenalUrl + ")") blockquoteText = blockquoteText.replace(mineUrl, "[" + mineUrl + "](" + mineUrl + ")") # 获取标签 tags = mainSoup.find_all('a', rel='tag') # print(tags) # 写入 md 文件 # 判断路径是否存在 if not os.path.exists(filePath + str(index) + '/'): os.makedirs(filePath + str(index) + '/') file = codecs.open(filePath + str(index) + '/' + articleTitle + '.md', "w", encoding='utf8') # 指定文件的编码格式 # 写入前置申明 file.write('---\n') file.write("title: " + articleTitle + '\n') file.write("date: " + createTime + '\n') file.write("date: " + updateTime + '\n') file.write("categories: " + '\n') for category in categoryName.split(','): file.writelines('- ' + category + '\n') file.writelines("tags: ") for tag in tags: tag = tag.text.replace('# ', '') file.writelines('- ' + tag + '\n') file.writelines('---' + '\n') # 写入引用块 if blockquote != None: file.writelines('> ' + blockquoteText) # 遍历正文块,写入文件,注意遍历文档树的时候 next_sibling 是紧紧接着的,比如这里是 \n,所以需要两个 # print(blockquote.next_sibling.next_sibling) for nextTag in body.children: # print(nextTag) # print(type(nextTag)) if type(nextTag) == bs4.element.NavigableString: continue tagName = '' codeType = '' codeStart = '' codeEnd = '' tagContent = nextTag.text.strip() if nextTag.name == 'h1': tagName = '# ' file.write(tagName + tagContent + '\n') continue if nextTag.name == 'h2': tagName = '## ' file.write(tagName + tagContent + '\n') continue if nextTag.name == 'h3': tagName = '### ' file.write(tagName + tagContent + '\n') continue if nextTag.name == 'h4': tagName = '##### ' file.write(tagName + tagContent + '\n') continue # 代码块 if nextTag.select('figure').__len__() > 0 or nextTag.name == 'figure': # 如果 select 的 length 大于 0 则表示这个元素是 包含 figure 的元素 if nextTag.select('figure').__len__() > 0: nextTag = nextTag.select('figure')[0] codeType = nextTag['class'][nextTag['class'].__len__() - 1] + '\n' codeStart = '``` ' codeEnd = '```\n' codeLine = '' lineNumber = nextTag.table.tr.find('td', attrs={'class': 'gutter'}).text code = nextTag.table.tr.find('td', attrs={'class': 'code'}).text tagContent = tagContent.replace(lineNumber, '').replace(code, '') # print(lineNumber) # print(code) # print(tagContent) for line in nextTag.table.tr.find('td', attrs={'class' : 'code'}).find_all('div'): codeLine += line.text.strip()+'\n' file.write(tagContent+'\n') file.write(codeStart + codeType + codeLine + '\n' + codeEnd) continue # 无序列表 if nextTag.name == 'ul': for li in nextTag.find_all('li'): file.write('- ' + li.text.strip() + '\n') continue # 有序列表 if nextTag.name == 'ol': olIndex = 1 for li in nextTag.find_all('li'): file.write(olIndex + '. ' + li.text.strip() + '\n') olIndex += 1 continue if nextTag.name == 'p': # 为空表示是图片 tagContent = nextTag.text.strip() if tagContent == '': file.write("![image](" + nextTag.find('img')['src'] + ")\n") continue else: links = nextTag.find_all('a') for link in links: tagContent = tagContent.replace(link.text, "[" + link['href'] + "](" + link['href'] + ")") file.write(tagContent + '\n') continue file.close() index = index+1
效果
第一页的文章
第二页的文章
第一篇文章,感觉效果还是不错的
代码
文章所有代码已经提交到 git
如有问题,希望不吝赐教!
- 使用爬虫进行一次 hexo 构建的博客爬取并且生成 md 文档
- 使用hexo生成博客
- hexo使用next主题,并且使用”多说“给博客的文章添加评论功能
- 博客利器Hexo的使用
- 一次python爬虫爬取p站的经历
- 使用markdown构建一次写作,无缝迁移的博客
- windows下使用python的scrapy爬虫框架,爬取个人博客文章内容信息
- Python爬虫小实践:使用BeautifulSoup+Request爬取CSDN博客的个人基本信息
- 【Hexo】Hexo+Github构建个人博客入门
- 使用Hexo构建自己的网站
- hexo系列教程:(三)hexo博客的配置、使用
- Hexo+github构建个人博客
- 【Python爬虫】爬取个人博客的图片
- 使用其他电脑时怎么对自己的hexo博客进行改动
- python 爬虫 爬取PyPDF2的官方在线文档
- 第一个Python爬虫,爬取某个新浪博客所有文章并保存为doc文档
- 使用hexo搭建自己的博客系统
- Hexo+Coding博客的安装和使用
- 对象间的联动——观察者模式(三)
- 小白笔记----------------------------------------------leetcode(107. Binary Tree Level Order Traversal I)
- windows那些时候让你感觉很无力
- 为什么许多公司不要培训机构出来的程序员?
- Android网络判断
- 使用爬虫进行一次 hexo 构建的博客爬取并且生成 md 文档
- 数据库+3级缓存+retrofit请求+fresco加载图片
- 编号满二叉树
- 面向对象编程的3种关系:组合,委托和继承
- PAT乙级 1007 素数对猜想 (20)
- 请求数据
- excel二级联动
- OpenCV Canny边缘检测
- c#七层 登录版