背景
一直非常喜欢一个网站 https://www.examples.com/,想着这个网站如果突然不在了可怎么办?就想着有什么办法能帮这个网站保存下来。
正好之前玩过Python爬虫,想着能不能帮网站爬下来,试试吧?
网站爬取
分析
我之前做爬虫一般需要分2步骤的,第一步是拿到要抓取目标站点的页面url,这一般都是独立算法的,第二步是抓取页面内容,然后分析页面内容拿到想要的数据。
- 获取完整网站地图
获取网站地图,我之前都是爬虫自动爬,就是给爬虫一个入口,然后分析这个入口页面的超链接,拿到所有的URL,然后判断下是不是本站的(防止爬虫跳到别的站点去了),然后去重之后放到一个集合里面,最后再看是深度优先还是广度优先递归的进行链接爬虫即可。
这样你就能拿到这个网站所有的URL数据库了。
但是我发现examples这个网站他自己有个自己站点所有文章的集合页 full_archive,我靠这就很简单了啊,因为我们其实爬虫最麻烦的就是爬URL的过程,这样一来我只要分析这个页面就能拿到这个网站的所有页面了。
打开这个页面,右键查看源文件,全选保存为本地HTML文件。
我写了个脚本,先贴上来再分析。
# -*- coding: utf-8 -*- # import requests from html.parser import HTMLParser import json hrefs = [] class MyHTMLParser(HTMLParser): _is_archive_list = False _is_a_tag = False def handle_starttag(self, tag, attrs): if len(attrs) == 1: if attrs[0][1] == "bca-archive__monthlisting": self._is_archive_list = True if tag == "a" and self._is_archive_list: self._is_a_tag = True print(attrs[0][1]) hrefs.append("https://www.examples.com" + attrs[0][1]) def handle_endtag(self, tag): if tag == "ul": self._is_archive_list = False if tag == "a": self._is_a_tag = False def handle_data(self, data): if self._is_archive_list and self._is_a_tag: print("Data: ", data) parser = MyHTMLParser() f = open("full_archive.html", "r") full_archive = f.read() parser.feed(full_archive) f.close() jsonString = json.dumps(hrefs) jsonFile = open("hrefs.json", "w") jsonFile.write(jsonString) jsonFile.close() print("hrefs len: " + str(len(hrefs)))
因为要分析HTML文档,用到了一个HTMLParser库,要装一下。
这个脚本主要是用HTMLParser库来分析页面,最好再把分析出来的URL存到本地一个json文件里面。
关键就是HTMLParser这个库的使用,很多年前用过,忘记怎么用的了。然后我随便看看官网,大概又想起来怎么用的了。
你要先parser = MyHTMLParser() ,拿到他的这个parser对象,然后帮你的html丢给他的feed方法就醒了。这个MyHTMLParser东西呢,是要继承HTMLParser这个函数的,然后他有几个方法让你重写,handle_starttag、handle_endtag、handle_data分别是当开始处理一个标签,结束处理一个标签和处理一个标签的内容。
这个玩意的小诀窍是你要定义一些flag,比如上面例子里面,开始一个标签 或者结束一个标签,判断下标签属性,如果是你要关注的,就打开这个flag。然后在别的地方就可以通过判断这个flag是否开启来决定要怎么处理数据了。
这样一来,我们就获取了一个包含这个网站所有页面的URL数据的hrefs.json文件了。
- 获取网站所有页面
那网站所有的页面URL数据都有了怎么来获取这个网站的页面数据呢?
这里提供下我写的另一个脚本
# -*- coding: utf-8 -*- import cfscrape import json import os from time import sleep f = open("hrefs.json", "r") hrefs = json.load(f) print(len(hrefs)) scraper = cfscrape.create_scraper() i=0 for href in hrefs: i=i+1 print(str(i)+": "+href) fileName = href[25:] filePath = "data/"+fileName+".html" os.makedirs(os.path.dirname(filePath), exist_ok=True) content = scraper.get(href).content f = open(filePath, "a") f.write(str(content, 'UTF-8')) f.close() sleep(0.1) f.close()
这个页面很简单,就是打开我们刚才保存链接的json文件,然后循环里面的URL数据,然后下载下来存到本地。
值得一提的时,我这里下载他们网站使用了一个叫cfscrape库,我的这篇文章有介绍,因为他们网站被Cloudflare保护的,你直接用request什么的库是没法帮网站下载下来的。
- 分析页面数据
这样所有我们想要的页面数据都下载到我们本地一个叫data的目录里面了,我们现在要做的就是遍历我们的文件目录,然后一个一个解析里面的数据了。
老规矩,直接上脚本
# -*- coding: utf-8 -*- import glob from html.parser import HTMLParser import json from bs4 import BeautifulSoup articles = [] for filename in glob.iglob('data/**', recursive=True): article={} f = open(filename, "r") full_archive = f.read() soup = BeautifulSoup(full_archive, 'html.parser') #title article['title'] = soup.find_all("h1", {"class": "single-title entry-title"})[0].contents[0] #categories categories = [] for tag in soup.find_all("a", {"rel": "category tag"}): categories.append({"href":tag['href'], "text":tag.text.strip()}) article['categories'] = categories #tags tags = [] for tag in soup.find_all("a", {"rel": "post tag"}): tags.append({"href":tag['href'], "text":tag.text.strip()}) article['tags'] = tags #updated article['updated'] = soup.find_all("span", {"class": "updated"})[0].contents[0] #content article['content'] = soup.find_all("section", {"class": "post-content clearfix"}) articles.append(article) f.close()
这里同样是要分析HTML文档,我用了个更省事的框架 BeautifulSoup4,看代码就知道了,他直接根据标签属性进行数据抓取,非常方便,最后拼装成我们要的数据放到我们的数组里面,就拿到所有我们要的数据了。
再根据我们的需要将这些格式化的数据持久化到本地文档就行啦。
注意:
https://www.examples.com/ 这其实是一个不存在的虚构网站,我真正抓取的站点没有贴出来,以防止一些不好的事情发生。