Python2实现简单的爬虫
前言
有时候我们需要一些网络数据来工作、学习,比如我们做深度学习的。当做一个分类任务时,需要大量的图像数据,这个图像数据如果要人工一个个下载的,这很明显不合理的,这是就要用到爬虫程序。使用爬虫程序帮我们下载所需要的图像。那么我们就开始学习爬虫吧。
爬虫的框架
整体框架
下图是爬虫的整体框架,其中包括调度端、URL管理器、网页下载器、网页解析器、价值数据,它们的作用如下:
调度端:
主要是调用URL管理器、网页下载器、网页解析器,也设置爬虫的入口;
URL管理器:
管理要爬网页的URL,添加新的URL,标记已爬过的URL,获取要爬的URL;
网页下载器:
通过URL下载网页数据,并以字符串保存;
网页解析器:
解析网页下载器获取到的字符串数据,获取用户需要的数据;
价值数据:
所有有用的数据都存储在这里。
下图是爬虫的一个顺序图,从顺序图中可以看出调度器通过训练调用URL管理器、网页下载器、网页解析器来不断获取网络数据。
URL管理器
如图所示,URL管理器是负责管理要爬取网页的URL的。当有新的URL,就把新的URL添加到管理器中,在添加之前还有判断URL是否已经存在。在获取时,先判断是否还有URL,如果有就提前URL并将它移动到已爬取的列表中。这样保证不添加新的重复的URL
网页下载器
从URL管理器中获取的URL,我们要把这些URL的网页数据下载下来,这是就要使用到了网页下载器,这说到下载的有本地文件或字符串,这是因为当我们爬取的是文件时,如图片,下载的就是文件了。当我们爬取的是网页中的内容数据时,这时就是字符串。
网页下载器的代码片段:
# coding=utf-8
import urllib2
url = "https://www.baidu.com"
response = urllib2.urlopen(url)
code = response.getcode()
content = response.read()
print "状态码:", code
print "网页内容", content
还可以添加请求头,模仿其他浏览器访问
# coding=utf-8
import urllib2
url = "https://www.baidu.com"
request = urllib2.Request(url)
# 模仿火狐浏览器
request.add_header("user-agent", "Mozilla/5.0")
response = urllib2.urlopen(request)
code = response.getcode()
content = response.read()
print "状态码:", code
print "网页内容", content
输出信息为:
状态码: 200
网页内容 <html>
<head>
<script>
location.replace(location.href.replace("https://","http://"));
</script>
</head>
<body>
<noscript><meta http-equiv="refresh" content="0;url=http://www.baidu.com/"></noscript>
</body>
</html>
网页解析器
在网页下载器中下载的众多字符串中,我们要提前我们需要的数据,如新的要爬取的URL、我们需要的网页数据。通过这个网页解析器就可以解析这些数据了。获取新的URL可以添加到URL管理器中,获取有用的数据就将它保存。
网页解析器的代码片段:
# coding=utf-8
from bs4 import BeautifulSoup
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story</b></p>
"""
soup = BeautifulSoup(html_doc, 'html.parser', from_encoding='utf-8')
# 寻找属性class为title的p标签
title_all = soup.find('p', class_="title")
print title_all
# 获取该标签对应的内容
title = title_all.get_text()
print title
输出信息如下:
<p class="title"><b>The Dormouse's story</b></p>
The Dormouse's story
爬虫程序
这个程序是爬取CSDN博客的文章,并爬取相关的文章。比如我们的爬虫入口是一篇《把项目上传到码云》的文章,在每章文章的最后都有相关的文章推荐,这些推荐的文章的URL就是我们补充的URL来源。如:
然后观察整个文章的网页源码,可以得到文章的标题的代码片段如下,关键定位信息是class="csdn_top"
:
<article>
<h1 class="csdn_top">把项目上传到码云</h1>
<div class="article_bar clearfix">
<div class="artical_tag">
<span class="original">
原创 </span>
<span class="time">2017年04月15日 20:39:02</span>
</div>
文章内容的代码片段如下,关键定位信息是class="article_content csdn-tracking-statistics tracking-click"
:
<div id="article_content" class="article_content csdn-tracking-statistics tracking-click" data-mod="popu_519" data-dsm="post">
<div class="markdown_views">
<p>一、为什么要使用码云而不使用GitHub?会有很多朋友这样问,原因有以下几条: <br>
推荐文章的代码片段如下,关键定位信息是strategy="BlogCommendFromBaidu_0"
:
<div class="recommend_list clearfix" id="rasss">
<dl class="clearfix csdn-tracking-statistics recommend_article" data-mod="popu_387" data-poputype="feed" data-feed-show="false" data-dsm="post">
<a href="https://blog.csdn.net/Mastery_Nihility/article/details/53020481" target="_blank" strategy="BlogCommendFromBaidu_0">
<dd>
<h2>上传项目到开源中国码云</h2>
<div class="summary">
上传项目到开源中国码云
</div>
有了这些定位,就可以开始爬取数据了,我们开始吧。
调度器
创建一个spider_mamin.py
文件来编写调度器的代码,这个就是调度中心,在这里控制整个爬虫程序:
# coding=utf-8
import html_downloader
import html_outputer
import html_parser
import url_manager
class SpiderMain(object):
# 调度程序
def __init__(self):
# 获取URL管理器
self.urls = url_manager.UrlManager()
# 获取网页下载器
self.downloader = html_downloader.HtmlDownloader()
# 获取网页解析器
self.parser = html_parser.HtmlParser()
# 获取数据输出器
self.output = html_outputer.HtmlOutput()
def craw(self, root_url, max_count):
count = 1
# 添加爬虫入口的跟路径
self.urls.add_new_url(root_url)
# 创建一个循环,如果URL管理器中还有新的URL就一直循环
while self.urls.has_new_url():
try:
# 从URL管理器中获取新的URL
new_url = self.urls.get_new_url()
print 'craw %d : %s ' % (count, new_url)
# 下载网页
html_cont = self.downloader.downloader(new_url)
# 解析网页数据
new_urls, new_data = self.parser.parser(new_url, html_cont)
# 添加新的URL
self.urls.add_new_urls(new_urls)
# 添加新的数据
self.output.collect_data(new_data)
# 满足爬取数量及中断
if count == max_count:
break
count = count + 1
except Exception, e:
print '爬取失败:', e
# 输出数据
self.output.output_html()
if __name__ == '__main__':
# 爬虫的根URL
root_url = "https://blog.csdn.net/qq_33200967/article/details/70186759"
# 爬取的数量
max_count = 100
obj_spider = SpiderMain()
# 启动调度器
obj_spider.craw(root_url, max_count)
URL管理器
创建一个url_manager.py
文件编写URL管理器的代码,添加新的URL和提供URL给网页下载器,由这个程序负责:
# coding=utf-8
class UrlManager(object):
# url管理器
def __init__(self):
self.new_urls = set()
self.old_urls = set()
# 向管理器中添加一个新的url
def add_new_url(self, url):
if url is None:
return
# 判断要添加的URL是否已存在新列表或者旧列表中
if url not in self.new_urls and url not in self.old_urls:
self.new_urls.add(url)
# 向管理器中添加批量url
def add_new_urls(self, urls):
if urls is None or len(urls) == 0:
return
for url in urls:
# 添加新的URL
self.add_new_url(url)
# 判断管理器中是否有新的待爬取的url
def has_new_url(self):
return len(self.new_urls) != 0
# 从url中获取一个新的待爬取的url
def get_new_url(self):
# 获取并移除最先添加的URL
new_url = self.new_urls.pop()
# 把这个路径添加到已爬取的列表中
self.old_urls.add(new_url)
return new_url
网页下载器
创建一个html_downloader.py
文件来编写网页下载器的代码,下载网页的字符串数据,都是HTML的代码:
# coding=utf-8
import urllib2
class HtmlDownloader(object):
# html下载器
def downloader(self, url):
# 如果路径为空就返回空
if url is None:
return None
# 打开网页数据
response = urllib2.urlopen(url)
# 判断是否访问成功,如果不成功就返回空
if response.getcode() != 200:
return None
# 返回网页数据
return response.read()
网页解析器
创建一个html_parser.py
文件来编写网页解析器的代码,从网页下载器获取的HTML格式的字符串中解析想要的数据个URL:
# coding=utf-8
import re
from bs4 import BeautifulSoup
class HtmlParser(object):
def parser(self, page_url, html_cont):
"""
# html解析器
:param page_url: 网页的URL
:param html_cont: 网页的字符串数据
:return: 网页包含相关的URL和文章的内容
"""
# 判断网页URL和网页内容是否为空
if page_url is None or html_cont is None:
return
# 获取解析器
soup = BeautifulSoup(html_cont, 'html.parser', from_encoding='utf-8')
# 获取解析到的URL
new_urls = self._get_new_urls(soup)
# 获取解析到的文章数据
new_data = self._get_new_data(page_url, soup)
return new_urls, new_data
# 解析相关文章的URL
def _get_new_urls(self, soup):
new_urls = set()
# 获取相关的文章URL,格式如下:
# <a href="https://blog.csdn.net/qq_18601953/article/details/78395878"
# target="_blank" strategy="BlogCommendFromBaidu_7">
links = soup.find_all('a', strategy=re.compile(r"BlogCommendFromBaidu_\d+"))
# 提取所有相关的URL
for link in links:
new_url = link['href']
new_urls.add(new_url)
return new_urls
# 解析数据
def _get_new_data(self, page_url, soup):
res_data = {}
# 获取URLurl
res_data['url'] = page_url
# 获取标题<h1 class="csdn_top">把项目上传到码云</h1>
essay_title = soup.find('h1', class_="csdn_top")
res_data['title'] = essay_title.get_text()
# 内容标签的格式如下:
# <div id="article_content" class="article_content csdn-tracking-statistics tracking-click"
# data-mod="popu_519" data-dsm="post">
essay_content = soup.find('div', class_="article_content csdn-tracking-statistics tracking-click")
res_data['content'] = essay_content.get_text()
return res_data
数据存储器
创建一个html_outputer.py
文件来编写存储数据的代码,当爬取完成数据之后,通过这个程序永久保存爬取的数据:
# coding=utf-8
class HtmlOutput(object):
#html输出器
def __init__(self):
self.datas = []
#收集数据
def collect_data(self, data):
if data is None:
return
self.datas.append(data)
#将收集好的数据写出到html文件中
def output_html(self):
fout = open('output.html','w')
fout.write("<html>")
fout.write("<body>")
fout.write("<table>")
if len(self.datas) == 0:
print "数据为空!"
#ascii
for data in self.datas:
fout.write("<tr>")
fout.write("<td>%s</td>" % data['url'])
fout.write("<td>%s</td>" % data['title'].encode('utf-8'))
fout.write("<td>%s</td>" % data['content'].encode('utf-8'))
fout.write("</tr>")
fout.write("</table>")
fout.write("</body>")
fout.write("</html>")
fout.close()
运行代码
运行调度器代码spider_mamin.py
,可以看到爬取过程输出的日志信息,如果出现失败是正常的:
craw 1 : https://blog.csdn.net/qq_33200967/article/details/70186759
craw 2 : https://blog.csdn.net/qq_18601953/article/details/78395878
craw 3 : https://blog.csdn.net/wust_lh/article/details/68068176
爬取完成之后,所有的数据都会以HTML格式存储在output.html
中。可以在浏览器中打开,如:
为了读者方便使用代码,我已将这些代码打包了,可以在这里下载完整代码。