Python爬虫实例- CSDN博客爬虫
2015-02-10 00:28
375 查看
Python爬虫实例: CSDN博客爬虫
Tags: Python 爬虫0x00 前言
关于爬虫原理、web基础等内容,请自行搜索。自己总结过一份,但觉得火候还不够,知道的内容感觉没什么必要写,想写的又总结不出来。在这推荐一份教程:Python爬虫入门教程。
下面将用Python实现CSDN博客爬虫:输入用户ID,将该用户所有博文存至本地数据库。
0x01 页面源码分析
CSDN博客地址URL结构为”http://blog.csdn.net/“+用户id,博客首页默认为摘要视图。点击第二页,易得某页的域名格式为”http://blog.csdn.net/“+用户id+”/article/list/”+页码
知道总页数后,即可自己构建所有目录页的URL。获取页数可从页面下方的统计数据获得
源码为
通过正则匹配即可,具体为
"([0-9]+)条数据"
在页面的HTML源码中,与博文有关的部分如下
每个
<div class="list_item article_item">元素对应一个博文的摘要。
进一步查看
每个list_item元素中有三个有用的子元素:
article_title
article_description
article_manage
article_title中有博文的相对地址,博文标题。
article_description中为博文摘要。
article_manage中右下角的时间、阅读次数、评论次数
从article_title中提取出相对地址
'<span class="link_title"><a href="([\s\S]*?)">\s*([\s\S]*?)\s*</a></span>',
[\s\S]表示任意字符,如果有其他简洁的写法还请留言告知:P
<div id="article_details" class="details">元素对应博文内容,将这一段内容提取出即可。
'(<div id="article_details" class="details">[\s\S]*?</div>)\s*<dl class="blog-associat-tag">'
<dl class="blog-associat-tag">为紧挨博文
<div id="article_details">的下一元素。
0x02 源码及注解
from optparse import OptionParser import queue import re import sqlite3 import threading import urllib.request import time import ThreadPool class Store(object): """结果存储类 每次操作完成后将结果存入队列,最后把结果统一存进数据库 """ def __init__(self, db_name, table_name="blog"): """初始化函数 Args: db_name: 数据库文件名 table_name: 数据库表名 """ threading.Thread.__init__(self) self._work_queue = queue.Queue() if db_name != None: self._dbname = db_name else: self._dbname = "default"+str(int(time.time()))+".db" self._tablename = table_name def add_item(self, title, user_id, content): """添加一条记录 Args: title: 博文标题 user_id: 博文作者id content: 博文内容 """ self._work_queue.put((title, user_id, content)) def store(self): """将所有记录存至数据库 """ conn = sqlite3.connect(self._dbname) c = conn.cursor() try: c.execute(("CREATE TABLE %s " "(TITLE TEXT PRIMARY KEY NOT NULL, " " USER_NAME TEXT NOT NULL, " " CONTENT TEXT NOT NULL)"%self._tablename)) except: pass purchases = [] while self._work_queue.qsize() != 0: purchases.append(self._work_queue.get()) c.executemany('INSERT INTO %s VALUES (?,?,?)'%self._tablename, purchases) conn.commit() conn.close() print("store end. dbfile name is %s" % (self._dbname)) def Download_URL(url, headers={}): """下载指定url页面 Args: url: 要下载页面的URL headers: headers为字典结构。键为HTTP请求的消息报头名,值为对应的报头值 Returns: 页面下载成功,返回对应的HTML。 Raises: None """ req = urllib.request.Request(urllib.request.quote(url, ":?=/&%"), headers=headers) while True: try: response = urllib.request.urlopen(req, timeout=5) # timeout设置超时时间 html = response.read().decode("utf-8") print("success: " + req.full_url) return html except Exception as reason: print(reason) def Store_Article(title, content): """存储文章 Args: title: 文章标题 content: 文章内容 """ global user_id global store # 新增一条要存储的结果 store.add_item(title, user_id, content) def Download_Article(param): """下载博文 Args: param: 字典。url键为博文地址,title键为文章标题 """ global user_id, headers article_url = param['url'] article_title = param['title'] # 通过正则匹配,返回博文内容。 # 随后保存博文内容,标题为:[作者ID]+博文名 article_html = Download_URL(article_url, headers) content = re.findall('(<div id="article_details" class="details">' '[\s\S]*?</div>)' '\s*<dl class="blog-associat-tag">', article_html)[0] Store_Article("[" + user_id + "]" + article_title, content) def Download_Page(param): """下载目录页 Args: param: 字典。url键中存有目录页的URL地址。 """ global headers, host global thread_pool page_url = param['url'] # 正则匹配,返回所有文章的链接和标题。格式为:(链接, 标题) # 随后下载每篇博文 page_html = Download_URL(page_url, headers) article_list = re.findall('<span class="link_title">' '<a href="([\s\S]*?)">' '\s*([\s\S]*?)\s*</a></span>', page_html) for article in article_list: param = { 'url': host + article[0], 'title': re.sub(r'[/:*?<|\\]', '', article[1]) + ".html" } thread_pool.add_work(Download_Article, param) def Download_Front_Page(param): """下载博客首页 Args: param: 字典。url键为博客首页地址。 """ global headers, user_id global thread_pool front_page_url = param["url"] # 通过正则匹配,获得页数,博文数。 # 随后下载所有目录页 html = Download_URL(front_page_url, headers) article_count = int(re.findall("([0-9]+)条数据", html)[0]) page_count = int(re.findall("共([0-9]+)页", html)[0]) print("id: " + user_id + "\n" + "article count: " + str(article_count) + "\n" + "page count: " + str(page_count)) for page_num in range(1, page_count+1): param = { "url": front_page_url + "/article/list/" + str(page_num) } thread_pool.add_work(Download_Page, param) def option_parser(): """解析命令行参数 Returns: (user_id, thread_count, db_name) 分别为用户ID,线程池线程个数,数据库文件名 """ user_id = None thread_count = 1 db_name = None parser = OptionParser() parser.add_option("-i", "--id", dest="user_id", help="blog owner id", metavar="[user id]") parser.add_option("-t", "--thread", dest="thread_count", help="worker thread count", metavar="[count of thread]") parser.add_option("-f", "--filename", dest="dbfile", help="data file name", metavar="[data file name]") (options, args) = parser.parse_args() if options.user_id != None: user_id = options.user_id else: print("eg: blog_backup.py -i csdn_id") exit() if options.thread_count != None: thread_count = int(options.thread_count) if options.dbfile != None: db_name = options.dbfile return (user_id, thread_count, db_name) def main(): global thread_pool, store global headers, host, user_id user_id, thread_count, db_name = option_parser() store = Store(db_name) thread_pool = ThreadPool.Thread_Pool(thread_count) host = "http://blog.csdn.net" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36" " (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36" } front_page_url = host + "/" + user_id param = { "url": front_page_url } # 向任务队列添加下载博客首页的任务 # 随后会自动解析下载所有目录页,进而解析下载所有博文 # 当所有下载任务均结束后,队列为空。在任务进行时队列不会为空。 # 所以可以通过等待队列为空进行线程同步 thread_pool.add_work(Download_Front_Page, param) thread_pool.wait_queue_empty() store.store() print("---end---") if __name__ == "__main__": main()
Python版本为3.4.2
ThreadPool为自己实现的线程池,详见Python线程池简单实现
通过命令行运行时,指定用户ID。程序先下载博客首页,获取博客页数,随后构建每一页的URL地址并下载,随后分析目录页源码,得到每篇博文的题目和URL,下载保存。
下载页面时,需指定
User-Agent字段。网上说这是因为robots.txt文件中显示禁止任何爬虫。但是,访问robots文件如下:
User-agent: * Disallow: /scripts Disallow: /public Disallow: /css/ Disallow: /images/ Disallow: /content/ Disallow: /ui/ Disallow: /js/ Disallow: /scripts/ Sitemap: http://blog.csdn.net/sitemap-index-1.xml[/code]
根据wiki百科的内容,robots协议并不具有强制性:
robots.txt协议并不是一个规范,而只是约定俗成的,所以并不能保证网站的隐私。
这个协议也不是一个规范,而只是约定俗成的,有些搜索引擎会遵守这一规范,而其他则不然。通常搜索引擎会识别这个元数据,不索引这个页面,以及这个页面的链出页面。
而且上述rotbots.txt文件只不允许访问特定目录,这些目录与博客首页如何产生联系,现在还没搞清。或许无法访问与robots协议并无关系?0x03 Tips
博客搬家应用(Java):http://m.oschina.net/blog/194507
自己动手编写CSDN博客备份工具(C++):http://blog.csdn.net/gzshun/article/category/932960
简单网络爬虫抓取博客文章及思想介绍(Python):http://blog.csdn.net/Eastmount/article/details/39770543
大部分博文是在2012年写的,感觉自己在玩别人玩剩下的= =
欢迎讨论:)
相关文章推荐
- Python开发实例分享bt种子爬虫程序和种子解析
- python抓取网页图片 python爬虫实例
- 以视频爬取实例讲解Python爬虫神器Beautiful Soup用法
- python爬虫实践学习实例之模拟登录
- python爬虫实践学习实例之mysql数据库操作
- Python学习笔记 第二部分 - 正则表达式 与 爬虫小实例(抓取豆瓣电影中评分大于等于8分的影片)
- Python网络爬虫实例讲解
- 以视频爬取实例讲解Python爬虫神器Beautiful Soup用法
- 自学Python之小爬虫实例
- 编写Python爬虫抓取暴走漫画上gif图片的实例分享
- python爬虫实例-爬取北邮研究生成绩
- python 爬虫(3) Opener与Handler的介绍和实例应用
- 简单的python爬虫抓取图片实例
- 使用Python的urllib和urllib2模块制作爬虫的实例教程
- python妹子图简单爬虫实例
- python3下几个淘宝、天猫、京东爬虫实例。(价格、销量、评论等)
- python爬虫实例(urllib&BeautifulSoup)
- python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例
- Python爬虫实例扒取2345天气预报
- 编写Python爬虫抓取暴走漫画上gif图片的实例分享