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写爬虫真的很方便

原创粉丝点击