新浪博客文章批量下载的Python脚本


Update @ 2015-6-14

建议有批量下载新浪博客文章需求的同学使用 https://github.com/bfishadow/SBB 来进行操作,一个简单的Python脚本,没有用到BeautifulSoup,甚至都没有用re,仅仅依靠urllib2模块下载,然后字符串进行截取操作就可以达到相同的目的,速度也比较快,不过缺点就是没有新建目录进行存放,所以在操作的时候要注意放在一个单独的目录中执行;有能力的同学可以自行下载该脚本进行一些修改,添加/修改一些功能方便自己使用。

> python SBB.py http://blog.sina.com.cn/chjguancha

在一个大牛的blog里面逛的时候看到他之前写的这个Python抓取脚本,觉得比较有趣而且有一定实际意义,就收藏了~~原始的脚本因为是2012年1月份左右写的,现在的BeautifulSoup这个模块也有了变化,所以稍微修改了一下,期间解决问题的过程值得记录,所以也就一并发出来了,做个记录:


首先,原文链接:

新浪博客批量下载:批量下载新浪博客的文章

其次,微调后的代码:
#!/usr/bin/env python
# coding=utf-8
import urllib2
import sys, os
import re
import string
from bs4 import BeautifulSoup

reload(sys)
sys.setdefaultencoding('utf8')

def encode(s):
    return s.decode('utf-8').encode(sys.stdout.encoding, 'ignore')

def getHTML(url):
    #proxy_handler = urllib2.ProxyHandler({'http':'http://211.138.124.211:80'})
    #opener = urllib2.build_opener(proxy_handler)
    #urllib2.install_opener(opener)
    req = urllib2.Request(url)
    response = urllib2.urlopen(req, timeout=15)
    return BeautifulSoup(response)

def visible(element):
    '''抓取可见的文本元素'''
    if element.parent.name in ['style', 'script', '[document]', 'head', 'title']:
        return False
    elif re.match('<!--.*-->', str(element)):
        return False
    elif element == u'xa0':
        return False
    return True

def delReturn(element):
    '''删除元素内的换行'''
    return re.sub('(?<!^)\n+(?!$)', ' ', str(element)).decode('utf-8')

def validFilename(filename):
    '''Windows'''
    return re.sub('[\/:*?<>"|\xa0]', '', filename)

def writeToFile(text, filename, dirname):
    if not os.path.exists(dirname):
        os.makedirs(dirname)
        print encode('保存到目录'), dirname

    filename = validFilename(filename)
    print encode('保存文章'), filename

    path = os.path.join(dirname, filename)
    if not os.path.exists(path):
        f = open(path, 'w')
        f.write(text)
        f.close()
    else:
        print filename, encode('已经存在')

def formatContent(url, title=''):
    '''格式化文章内容'''
    page = getHTML(url)
    content = page.find('div', {'class':'articalContent'})
    art_id = re.search('blog_(\w+)\.html', url).group(1)
    blog_name = str(page.find('title')).split('_')[-2]

    if title == '':
        title = str(page.find('title')).split('_')[0][7:]

    temp_data = filter(visible, content.findAll(text=True)) # 去掉不可见元素
    temp_data = ''.join(map(delReturn, temp_data)) # 删除元素内的换行符
    temp_data = temp_data.strip() # 删除文章首尾的空行
    temp_data = re.sub('\n{2,}', '\n\n', temp_data) # 删除文章内过多的空行

    # 输出到文件
    # 编码问题
    temp_data = '本文地址:'.decode('utf-8') + url + '\n\n' + temp_data
    op_text = temp_data.encode('utf-8')
    op_file = title + '_' + art_id +'.txt'

    writeToFile(op_text, op_file, blog_name)

def articlelist(url):
    articles = {}
    page = getHTML(url)
    pages = page.find('ul', {'class':'SG_pages'}).span.string
    page_num = int(re.search('(\d+)', pages).group(1))

    for i in range(1, page_num+1):
        print encode('生成第%d页文章索引'%i)
        if i != 1:
            url = re.sub('(_)\d+(\.html)$', '\g<1>'+str(i)+'\g<2>', url)
            page = getHTML(url)

        article = page.findAll('span', {'class':'atc_title'})
        for art in article:
            art_title = art.a['title']
            art_href = art.a['href']
            articles[art_title] = art_href

    return articles

def blog_dld(articles):
    if not isinstance(articles, dict):
        return False
    print encode('开始下载文章')
    for art_title, art_href in articles.items():
        formatContent(art_href, art_title)

if __name__ == '__main__':
    sel = raw_input(encode('你要下载的是(1)全部文章还是(2)单篇文章,输入1或者2: '))

    if sel == '1':
        #articlelist_url = 'http://blog.sina.com.cn/s/articlelist_1303481411_0_1.html'
        articlelist_url = raw_input(encode('请输入博客文章目录链接: '))
        articles = articlelist(articlelist_url)
        blog_dld(articles)
    else:
        #article_url = 'http://blog.sina.com.cn/s/blog_4db18c430100gxc5.html'
        article_url = raw_input(encode('请输入博客文章链接: '))
        formatContent(article_url)
以下是我当时解决问题的过程记录:
第一次报错:ImportError: No module named BeautifulSoup

因为新版本的BeautifulSoup已经不再命名为BeautifulSoup了,而是bs4,所以需要先下载安装bs4模块,然后在代码中将对应的import命令修改为:from bs4 import BeautifulSoup

参考文章:

第二次报错:

 

> python sinablog_download.py
你要下载的是(1)全部文章还是(2)单篇文章,输入1或者2: 1
请输入博客文章目录链接: http://blog.sina.com.cn/s/articlelist_1852149607_0_1.html
Traceback (most recent call last):
    File "sinablog_download.py", line 116, in <module>
        articles = articlelist(articlelist_url)
    File "sinablog_download.py", line 83, in articlelist
        page = getHTML(url)
    File "sinablog_download.py", line 19, in getHTML
        return BeautifulSoup(response, convertEntities=BeautifulSoup.HTML_ENTITIES)
AttributeError: type object 'BeautifulSoup' has no attribute 'HTML_ENTITIES'

搜索“AttributeError: type object ‘BeautifulSoup’ has no attribute ‘HTML_ENTITIES’”,原来是因为“BeautifulSoup构造函数不再承认smartQuotesTo或convertEntities等参数,默认情况下直接转换成Unicode”,所以将对应代码处修改为:
return BeautifulSoup(response),即直接去掉后面的参数。

参考文章:

第三次报错:
> python sinablog_download.py
你要下载的是(1)全部文章还是(2)单篇文章,输入1或者2: 1
请输入博客文章目录链接: http://blog.sina.com.cn/s/articlelist_1852149607_0_1.html
Traceback (most recent call last):
    File "sinablog_download.py", line 116, in <module>
        articles = articlelist(articlelist_url)
    File "sinablog_download.py", line 84, in articlelist
        pages = page.find('ul', {'class':'SG_pages'}).span.string
AttributeError: 'NoneType' object has no attribute 'string'

这里没有直接在网上找到对应的错误说明,但是,因为这一错误对应的代码涉及到了字符串查找命令,然后其中的NoneType这个关键字也比较易懂,于是我就去查看网页源代码,发现在页面“http://blog.sina.com.cn/s/articlelist_1852149607_0_1.html”的源码中查找“SG_pages”关键字时,那一段内容为空,然后我就懂了,因为我选的博文目录中只有4篇文章,根本就没有翻页的功能,可能是这个地方错了。于是去网上找了个文章多的新浪博客,然后查看网页源代码,运行了一下果然还是可以的。
对比:

http://blog.sina.com.cn/s/articlelist_1852149607_0_1.html

http://blog.sina.com.cn/s/articlelist_1494759712_0_1.html

在网页源码中查找“SG_pages”然后就可以直观看到问题所在。

第四次报错:
> python sinablog_download.py
你要下载的是(1)全部文章还是(2)单篇文章,输入1或者2: 1
请输入博客文章目录链接: http://blog.sina.com.cn/s/articlelist_1494759712_0_1.html
生成第1页文章索引
生成第2页文章索引
生成第3页文章索引
生成第4页文章索引
生成第5页文章索引
生成第6页文章索引
生成第7页文章索引
生成第8页文章索引
生成第9页文章索引
生成第10页文章索引
生成第11页文章索引
生成第12页文章索引
生成第13页文章索引
生成第14页文章索引
生成第15页文章索引
生成第16页文章索引
生成第17页文章索引
生成第18页文章索引
生成第19页文章索引
开始下载文章
Traceback (most recent call last):
    File "sinablog_download.py", line 117, in <module>
        blog_dld(articles)
    File "sinablog_download.py", line 108, in blog_dld
        formatContent(art_href, art_title)
    File "sinablog_download.py", line 67, in formatContent
        temp_data = filter(visible, content.findAll(text=True)) # 鍘绘帀涓嶅彲瑙佸厓绱?
    File "sinablog_download.py", line 25, in visible
        elif re.match('<!--.*-->', str(element)):
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-6: ordinal not in range(128)

搜索错误原因“UnicodeEncodeError: ‘ascii’ codec can’t encode characters in position 0-6: ordinal not in range(128)”,可以很直接的找到解决方法:

添加如下代码:

import sys
reload(sys)
sys.setdefaultencoding('utf8')

然后就可以正常执行了。

参考文章:


完整测试:
> python sinablog_download.py
你要下载的是(1)全部文章还是(2)单篇文章,输入1或者2: 1
请输入博客文章目录链接: http://blog.sina.com.cn/s/articlelist_1494759712_0_1.html
生成第1页文章索引
...
开始下载文章
保存到目录 月光博客
保存文章 网络地图国家队“天地图”卫星图片疑与谷歌地图同源_591839200100n1zv.txt
...

对单篇文章的下载就很顺利了{脚本会分别为每一个博客作者新建一个目录,然后将下载的txt文件放在该目录中}。

,

《 “新浪博客文章批量下载的Python脚本” 》 有 3 条评论

  1. 你的代码粘贴到网站上的时候好像很多backslash(”)都被escape掉了~搞到我总是出错。。。。一个一个地把正则表达式又全部修正过来才行。。。

    • 不好意思,该文章是去年记录的,当时可能在本地测试了之后放到网站上的过程中出了些纰漏所以导致部分内容被转义了,对产生你的困扰表示抱歉!
      近期比较忙,没有太多时间进行整理,待周末了再好好编辑/修补一下之前记录的内容,以免给人带来不便,谢谢理解。

    • Hi,刚才抽时间更新了文章的内容,不过代码本身的实用性已经不高了,主要记录的是当时处理该问题的过程,算是个回忆;
      如果有批量下载新浪博客文章的需要可以考虑一下 https://github.com/bfishadow/SBB ,代码很简单,但效果还不错,也易于修改;
      最后,谢谢指正。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注