python爬虫实战(一)
来源:互联网 发布:淘宝商城cma检测 编辑:程序博客网 时间:2024/06/06 05:06
看了网上好多人写的爬虫,架构风格都不是很喜欢,前几天在GitHub上翻到一个项目,主要是结构特别好,那种面向对象的风格很受我的喜欢,今天按照这种方式写了两个爬虫分享给大家
废话不多说,直接上代码
一.利用requests,BeautifulSoup库爬取CSDN上的1000篇博客
一共四个文件:
1.spider_mian:调度器
import refrom CSDN_spider import html_parser, save_txt, html_downloader#爬虫总调度器class SpiderMain(object): #构造方法初始化下载器,解析器,存储器 def __init__(self): self.downloader = html_downloader.Downloader() self.html_parser = html_parser.Parser() self.saver = save_txt.Saver() #爬虫方法,构造参数:开始网址,要爬取的文章数量,和页面号 def crawl(self,root_link,article_count,page_num): count = 1 link = root_link #循环迭代,直到爬到一定数量的文章 while 1: #通过下载器,解析器得到网页上所有文章链接 page = self.downloader.download(link) urls = self.html_parser.parse_link(page) #对每个文章的连接进行爬虫 for url in urls: print('crawl %d:%s' % (count ,url)) article_html = self.downloader.download(url) article_done = self.html_parser.parse_article(article_html) #判断文章为空,则不进行存储 if article_done is not None: self.saver.save_article(article_done,count,url) count += 1 if count==article_count: break if count == article_count: break #进行下一次的爬取 page_num += 1 new_link = re.sub(r'p=\d+','p=%d'%page_num,link) link = new_linkif __name__ == '__main__': # root_link = 'http://so.csdn.net/so/search/s.do?p=1&q=Lucene&t=blog&domain=&o=&s=&u=&l=&f=&rbg=0' # root_link = ' http://so.csdn.net/so/search/s.do?p=2&q=%E5%A4%A7%E6%95%B0%E6%8D%AE&t=blog&domain=&o=&s=&u=&l=&f=&rbg=0' root_link = 'http://so.csdn.net/so/search/s.do?p=1&q=%E4%BA%91%E8%AE%A1%E7%AE%97&t=blog&o=&s=&l=' article_count = 1000 page_num = 1 spider = SpiderMain() spider.crawl(root_link,article_count,page_num)
将爬虫的总调度程度单独写在一个类中,这样可维护性,可读性都比较好
2.html_downloader:下载器
import requestsclass Downloader(object): #下载方法 def download(self, url): if url is None or len(url)==0: return None try: #注意要改变头信息 r = requests.get(url,headers={'user-Agent':'Mozilla/5.0'}) r.raise_for_status() r.encoding=r.apparent_encoding return r.text except: print('download error')
3.html_parser:解析器
import refrom bs4 import BeautifulSoupclass Parser(object): #解析网页的方法 def parse_link(self, page): #这里养成习惯做判断 if page is None: return None soup = BeautifulSoup(page,'html.parser') #用beautifulsoup+正则获取链接 links = soup.find_all('a',href=re.compile(r'.*/article/details/\d*')) #用set防止重复链接 urls = set() for link in links: urls.add(link['href']) return urls #解析文章的方法 def parse_article(self, article_html): if article_html is None: return None try: #分别解析文章的标题和内容,以字典的形式存储 soup = BeautifulSoup(article_html, 'html.parser') article_done = {} title = soup.find('h1',class_='csdn_top').get_text() article_done['title'] = title content = soup.find('div',id='article_content').get_text() article_done['content'] = content return article_done except: return None
4.Saver:存储器
class Saver(object): def save_article(self, article_done,num,url): #注意改变编码 with open('./article/'+str(num)+'.txt','w',encoding='utf-8') as f: f.writelines('title:'+article_done['title']+'\n') f.writelines('url:'+url+'\n') f.writelines('content:'+article_done['content'])
我都在代码中写了详细的注释,应该能看懂
5.踩过的坑儿
这个爬虫很简单,写的话用不了多长时间,但是还是有一些细节问题让我调试程序也花费了好多时间
1.在每个解析或下载的方法中,一定要判断传进来的参数是否为空,否则很可能发生异常,引发爬虫中断。
2.容易引发异常的地方用try except 处理,防止爬虫中断
3.装入url一定要用set!因为一个页面中可能有不同的url,这个我真的查了好久才发现
4.爬虫的时候要有良好的用户提示,输出进度,一直等着很蛋疼!
5.注意修改头信息,有的网站反爬虫技术,不让爬,这个要养成习惯
6.注意修改爬虫response对象编码,具体参照我上面的代码
7.要修改输出文件的编码,utf-8
8.养成良好的代码风格,将不同功能的模块分离开,就像上面我写的这种,一个类负责一个功能模块
二.爬取百度百科的1000个词条信息
1.spider_main
class SpiderMain(object): #初始化信息,这里加一个url管理器 def __init__(self): self.urls = url_manager.UrlManager() self.downloader = html_downloader.Downloader() self.parser = html_parser.Parser() self.outputer = html_outputer.Outputer() def crawl(self,root_url): #将根路径添加到url管理器中 self.urls.add_url(root_url) count = 1 #迭代爬取,直到满足条件 while self.urls.has_new_url(): #从url管理器得到新的url new_url = self.urls.get_new_url() #输出进度 print('crawl %d:%s'%(count,new_url)) #下载器下载内容 html_cont = self.downloader.download(new_url) #解析器解析内容 new_urls ,new_data = self.parser.parse(new_url,html_cont) #将解析的url添加到url管理器中 self.urls.add_urls(new_urls) #将解析后的内容加入处理器 self.outputer.collect(new_data) if(count==1000): break count += 1 self.outputer.output()if __name__ == '__main__': root_url = 'https://baike.baidu.com/item/蜘蛛/6152'; obj_spider = SpiderMain() obj_spider.crawl(root_url)
2.html_downloader
这部分代码跟上面没有太大的变化,就不写注释了
class Downloader(object): def download(self, new_url): if new_url is None or len(new_url)==0: return None try: r = requests.get(new_url,headers={'user-Agent':'Mozilla/5.0'}) r.raise_for_status() r.encoding = r.apparent_encoding return r.text except: print('download error')
3.url_manager
这里新加了个url管理器,这个类负责对所有的类进行管理,这样就可以迭代进行爬取,我觉得有点像广度优先搜索的意思,只不过这里我们用一个集合来维护
class UrlManager(object): def __init__(self): #注意这里要用集合,防止有同样的url self.new_urls = set() self.old_urls = set() #取出url def get_new_url(self): url = self.new_urls.pop() self.old_urls.add(url) return url #判断是否有新的url def has_new_url(self): return len(self.new_urls) != 0 #添加新的url def add_urls(self, new_urls): if new_urls is None or len(new_urls)==0: return for url in new_urls: self.new_urls.add(url) #添加原始url def add_url(self, root_url): if root_url is None: return if root_url not in self.new_urls and root_url not in self.old_urls: self.new_urls.add(root_url)
4.html_parser
import refrom urllib.parse import urljoinfrom bs4 import BeautifulSoupclass Parser(object): #解析url,解析出新的url和要爬取的内容,这里为了解耦也将这两个方法分开写 def parse(self, new_url, html_cont): if new_url is None or html_cont is None: return soup = BeautifulSoup(html_cont,'html.parser') new_urls = self.get_urls(soup,new_url) new_data = self.get_data(soup, new_url) return new_urls,new_data def get_urls(self, soup, url): new_urls = set() links = soup.find_all('a',href=re.compile(r'/item/(.*)')) #对爬取的链接循环加入 for link in links: new_url = link['href'] #这个方法很好用,自动拼接,推荐大家使用 full_url = urljoin(url,new_url) new_urls.add(full_url) return new_urls # < dd class ="lemmaWgt-lemmaTitle-title" > #<div class="lemma-summary" label-module="lemmaSummary"> def get_data(self, soup, new_url): #用字典存储 data = {} title_node = soup.find('dd',class_="lemmaWgt-lemmaTitle-title").find('h1') #bs提供了大量的方法来获取你想要的内容 data['title'] = title_node.get_text() summary_node = soup.find('div',class_='lemma-summary') data['summary'] = summary_node.get_text() return data
5.outputer
这部分可以根据需要随便写,我这里是输出到本地文件中
class Outputer(object): def collect(self, new_data): with open('baidu.txt','a',encoding='utf-8') as f: f.writelines(new_data['title']+':'+'\n') f.writelines(new_data['summary']+'\n')
人生苦短,我用python,python写爬虫真的很方便
- Python爬虫实战(一)
- python爬虫实战(一)
- python网络爬虫实战笔记(一)
- Python爬虫入门实战一
- Python实战(一)——Python编写网络爬虫
- Python爬虫实战之抓取淘宝MM照片(一)
- python爬虫入门 实战(一)---爬糗事百科
- python爬虫实战(一)--爬取知乎话题图片
- Python 爬虫实战(一):使用 requests 和 BeautifulSoup
- python爬虫实战(一)----------爬取京东商品信息
- Python爬虫实战(二)
- python爬虫实战(四)
- Python爬虫(一)
- Python爬虫(一)
- python爬虫(一)
- Python爬虫(一)
- python爬虫(一)
- python爬虫(一)
- Kotlin-2.3-接口
- Faster RCNN解析
- 立贴为誓 好不好
- SpringMVC 后台接口,使用axios 传递参数接收不到
- 二、maven+web整合ssm框架
- python爬虫实战(一)
- 关于Android图片处理的总结
- Jmeter
- gf14 IP数字后端设计
- MPAndroidChart LineChart X轴标签显示问题
- 栈与队列
- Roman to Integer
- Kotlin-2.4-可见性修饰符
- Android 定期动态更改启动页