热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Python爬虫学习笔记(二)

爬虫接触了也有段时间,跟着网上的一些教程,不仅做出了一些实用的小工具,而且对于使用Python爬虫的整个流程有了大致的了解,也知道了爬虫是怎么回事。以前做的一些小的试验,陆续也都会

爬虫接触了也有段时间,跟着网上的一些教程,不仅做出了一些实用的小工具,而且对于使用Python爬虫的整个流程有了大致的了解,也知道了爬虫是怎么回事。以前做的一些小的试验,陆续也都会写成博客,今天记录的, 是我在慕课网上(http://www.imooc.com/learn/563)学到的一个爬虫框架,结构清晰合理,很值得学习,这里实现的只是爬虫最简单的功能,不涉及用户的登陆和COOKIE验证,当然,这些都是可以扩充到这个框架里的。

首先,先讲一讲整个爬虫框架的思路。

先确定任务:就是以百度百科Python词条为根,陆续爬下相关的1000个网页,这个阈值自然是可以调整的。都知道,在百度百科页面,很多关键词都是有链接的,链接到另一个网页上,可以搜索到更多的链接,就这样循环爬取,直到1000个为止。

代码大概用了150多行,写成了五个对象,SpiderMain、UrlManger、HtmlDownloader、HtmlParser和HtmlOutputer。SpiderMain是所有其余对象的入口,UrlManger负责管理url,用了两个set(),一新一旧,因为已经使用过的链接就没有必要再爬一次了,就放在old里,从网页中解析出来的新的链接放在new里。HtmlDownloder负责将网站的数据下载到本地,HtmlParser负责把下载到本地的网页数据里的相关数据解析出来,这里解析的,一个是链接,另一个是词条的summary,HtmlOupter就负责最后结果的输出。整个框架对应的是一个线性的流程,结构清晰。

下面看代码。

# 实例:爬取python相关一千个网址

 

# 先导入有关的库,这一步很重要

import urllib2

from bs4 import BeautifulSoup # 很好用的解析器

import re # 很容易遗漏,即使漏掉程序也不报错,很隐蔽

 

# 程序的入口类

class SpiderMain(object):

# 初始化对象

def __init__(self):

self.urls = UrlManger()

self.downloader = HtmlDownloader()

self.parser = HtmlParser()

self.outputer = HtmlOutputer()

 

# 最核心的方法

def craw(self, root_url):

count = 1 # 方便计数

self.urls.add_new_url(root_url)

while self.urls.has_new_url():

try:

# 取出url

new_url = self.urls.get_new_url()

print 'craw %d : %s' % (count, new_url)

# 根据url下载网页内容

html_cOnt= self.downloader.download(new_url)

# 将网页内容解析为需要的数据

new_urls, new_data = self.parser.parse(new_url, html_cont)

# 将数据各自存放

self.urls.add_new_urls(new_urls)

self.outputer.collect_data(new_data)

 

if count == 10:

break

 

count += 1

 

except:

print 'craw failed'

 

# 将数据输出为html文件

self.outputer.output_html()

 

 

 

class UrlManger(object):

 

# 初始化,两个set(),因为set()里的数据不会重复

def __init__(self):

self.new_urls = set()

self.old_urls = set()

 

# 添加一个url

def add_new_url(self, url):

if url is None:

return

if url not in self.new_urls and url not in self.old_urls:

self.new_urls.add(url)

 

# 添加多个url,循环使用add_new_url,很巧妙

def add_new_urls(self, urls):

if urls is None or len(urls) == 0:

return

for url in urls:

self.add_new_url(url)

 

# 判断是否还有没有爬取的url,返回bool值

def has_new_url(self):

return len(self.new_urls) != 0

 

# 获取一个url

def get_new_url(self):

new_url = self.new_urls.pop() # pop用的极好,不仅取得了数据,而且将元素从set()中删除

self.old_urls.add(new_url) # 使用过的url要放在old_urls里,避免重复爬取

return new_url

 

# 下载器

class HtmlDownloader(object):

 

def download(self, url):

if url is None:

return None

 

# Python自带的urllib2下载模块

respOnse= urllib2.urlopen(url)

 

# 可以用getcode()方法判断是否下载成功

if response.getcode() != 200:

return None

 

return response.read()

 

# Html解析器,核心,倒着看

class HtmlParser(object):

 

def get_new_urls(self, soup):

 

# 解析出来的url也放在一个set()里

new_urls = set()

# 正则表达式是根据网页源代码分析出来的

links = soup.find_all('a', href=re.compile(r'/view/\d+\.htm'))

for link in links:

new_url = link['href']

# 解析出来的链接是个相对url,要合成成一个绝对url

page_url = 'http://baike.baidu.com'

new_full_url = page_url + new_url

new_urls.add(new_full_url)

return new_urls

 

def get_new_data(self, page_url, soup):

 

# 先构造一个字典

res_data = {}

 

# url

res_data['url'] = page_url

 

#

Python

title_node = soup.find('dd', class_='lemmaWgt-lemmaTitle-title').find('h1')

res_data['title'] = title_node.get_text()

 

#

summary_node = soup.find('div', class_='lemma-summary')

res_data['summary'] = summary_node.get_text()

 

return res_data

 

def parse(self, page_url, html_cont):

 

if page_url is None or html_cont is None:

return

 

# BeautifulSoup固定用法,先构造一个BeautifulSoup对象

soup = BeautifulSoup(html_cont, 'html.parser', from_encoding='utf-8')

 

# 方法里面嵌套方法,值得学习

new_urls = self.get_new_urls(soup)

new_data = self.get_new_data(page_url, soup)

return new_urls, new_data

 

# 输出器

class HtmlOutputer(object):

 

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('D:/Learn/Code/python/pachong/output.html', 'w')

 

# 这一句必须加上,否则中文字符会显示乱码

fout.write('')

fout.write('')

fout.write('')

fout.write('

')

fout.write('

')

fout.write('

')

fout.write('

')

 

for data in self.datas:

fout.write('

')

fout.write('

' % data['url'])

# 注意中文字符的编码

fout.write('

' % data['title'].encode('utf-8'))

fout.write('

' % data['summary'].encode('utf-8'))

fout.write('

')

 

fout.write('

URL Title Summary
%s %s %s
')

fout.write('')

fout.write('')

 

# 不要忘记关闭文件

fout.close()

 

# 初始化程序

root_url = 'http://baike.baidu.com/view/21087.htm'

obj_spider = SpiderMain()

obj_spider.craw(root_url)

结果如下:

Python爬虫学习笔记(二)

打开output.html

Python爬虫学习笔记(二)

没什么问题!

 

总结:虽然名为一个简易的爬虫框架,其实已经不简单,难得的是思路如此清晰,结构很严谨,很多细节都很值得玩味。就凭这一个项目,也能看得出自己与高手之间的差距。从这些代码中,我领悟到,在写代码之前,一定要先理清思路;其次,这里用到了面向对象(oop)的方式,功能各自独立,又互相补充,这不就是高内聚、低耦合吗?要学的还有很多!

 

源代码地址:https://github.com/Lucifer25/Learn-Python/blob/master/Web-Crawler/SimpleFramework.py

 

参考资料:http://www.imooc.com/learn/563


推荐阅读
  • ASP.NET2.0数据教程之十四:使用FormView的模板
    本文介绍了在ASP.NET 2.0中使用FormView控件来实现自定义的显示外观,与GridView和DetailsView不同,FormView使用模板来呈现,可以实现不规则的外观呈现。同时还介绍了TemplateField的用法和FormView与DetailsView的区别。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文讨论了如何在不使用SearchBar display controller的情况下,单独使用SearchBar并捕获其textChange事件。作者介绍了实际状况,即左侧SliderMenu中的SearchBar需要在主页TableView中显示搜索结果。然后,作者提供了解决方案和步骤,帮助读者实现这一功能。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 展开全部下面的代码是创建一个立方体Thisexamplescreatesanddisplaysasimplebox.#Thefirstlineloadstheinit_disp ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 不同优化算法的比较分析及实验验证
    本文介绍了神经网络优化中常用的优化方法,包括学习率调整和梯度估计修正,并通过实验验证了不同优化算法的效果。实验结果表明,Adam算法在综合考虑学习率调整和梯度估计修正方面表现较好。该研究对于优化神经网络的训练过程具有指导意义。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • FeatureRequestIsyourfeaturerequestrelatedtoaproblem?Please ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
author-avatar
少唇_200
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有