邓作恒的博客 +

Scrapy实现简单爬虫抓取旧站数据

1.准备工作

首先你得知道爬虫是怎么工作的,CSDN有篇Python爬虫入门教程写得还不错,我就是参考这篇编写的,因为院学生会的旧站用的是古(keng)典(die)的table布局,用正则表达式提取估计非常痛苦,所以看完基础教程后果断选择Scrapy框架.

为了使用Scrapy,首先你得安装Scrapy,博主参考上文中CSDN教程中的安装方法还算成功:

  1. 安装Python
    建议Python 2.7.X,因为Python3在各种库中支持情况都不太统一,另外建议安装32位,下面一些部件中可能没有64位,安装完后记得在环境变量中配置Python目录和Python\Script目录,然后在cmd中输入python,如果输出版本信息,说明环境变量配置成功.

  2. 安装lxml
    到https://pypi.python.org/pypi/lxml/3.3.1选择对应版本安装即可

  3. 安装setuptools
    https://pypi.python.org/packages/2.7/s/setuptools/

  4. 安装Twisted
    http://twistedmatrix.com/trac/wiki/Downloads

  5. 安装zope.interface
    https://pypi.python.org/pypi/zope.interface/4.1.0#downloads

  6. 安装pyOpenSSL
    https://launchpad.net/pyopenssl

  7. 安装win32py
    http://sourceforge.net/projects/pywin32/files/pywin32/Build%20219/

  8. 安装Scrapy
    安装了上面这些部件,只需在cmd上执行easy_install scrapy即可,安装完成后执行scrapy检查,出现版本信息自然是安装成功了.

上面的过程与Scrapy官方文档的安装指南有不同,官方安装指南安装的是pip,但是我安装pip后安装Scrapy总是出错,原因至今没找到,于是用了CSDN那篇教程的安装方法

2.抓取网页

Scrapy应该默认就是多线程的,所以抓取过程很快,Scrapy项目的建立和运行可参考官方文档,这里不赘述. 抓取过程中可能出现编码问题,网上找的[1]解决方案是在python\lib\site-packages下新建sitecustomize.py:

import sys
sys.setdefaultencoding('utf-8')
#不行就设为gb3132试试呗

接下来要正经地抓网页了,比如,要把旧站新闻中心的所有新闻链接抓下来,首先你得写一个爬虫:

from scrapy.spider import Spider
from scrapy.selector import Selector 

class TwxshSpider4NewsURL(Spider):
    name="twxshspider4newsurl"
    allowed_domains=["http://eic.jnu.edu.cn/"]
    start_urls=[
        "http://eic.jnu.edu.cn/twxsh/channels/101.html",
        "http://eic.jnu.edu.cn/twxsh/channels/101_2.html",
        "http://eic.jnu.edu.cn/twxsh/channels/101_3.html",
        "http://eic.jnu.edu.cn/twxsh/channels/101_4.html"]

    def parse(self,response):
        sel=Selector(response)
        urls=sel.xpath('body/table[3]/tr[2]/td[1]/table[1]/tr[1]/td[3]/table[1]/tr[2]/td[1]/table/tr[1]/td[1]/a/@href')
        
        filename=response.url.split('/')[-1]
        f=open(filename,'wb')
        for url in urls:
            f.write('http://eic.jnu.edu.cn'+url.extract()+'\r\n')
        f.close

这里的start_urls是我自己打开网页复制下来的,因为只有几页,再写一个爬虫抓的话效率就低了.

Scrapy自动对返回体调用你实现的parse方法来处理,要提取有用的信息就要在这里实现了.比如这里就用xpath选择器选择了body下的第3个table的第2个tr…..因为这网页就全是table,我也没办法,只能自己下载一个网页,整理好格式,一个一个table地数,然后找到对应的a标签提取其href属性.

xpath的用法可以找w3school的XML教程看看就好,知道它有与jQuery选择器相同甚至之上的表达能力就行了.

抓取到目标的url后,就可以根据这些url提取正文了.过程跟上面的差不多,就直接贴代码了,需要注意的地方后面会特别指出:

# -*- coding: utf-8 -*- 
from scrapy.spider import Spider
from scrapy.selector import Selector
import re
import thread
import urllib2
import os

#读取要爬的URL
def read_urls(filename):
    lst=[]
    f=open(filename)
    while True:
        line=f.readline()
        if not line:
            break
        line=line.split('html')[0]+'html'
        lst.append(line)
    f.close()
    return lst
#下载图片
def downloadimg(url,out_put_path):
    socket=urllib2.urlopen(url)
    data=socket.read()
    with open(out_put_path,'wb') as jpg:
        jpg.write(data)
    socket.close()
#主爬虫
class TwxshSpider4NewsText(Spider):
    name="twxshspider4newstext"
    allowed_domains=["http://eic.jnu.edu.cn/"]
    start_urls=read_urls('newsurl.txt')

    def parse(self,response):
        #新建各种文件和文件夹
        new_dir_name=response.url.split('/')[-1].split('.html')[0]
        
        cur_path='newstext\\'
        new_path=os.path.join(cur_path,new_dir_name)
        if not os.path.isdir(new_path):
            os.makedirs(new_path)
        #新建图片文件夹,用于储存图片
        img_path=os.path.join(new_path,'images')
        os.makedirs(img_path)

        filename=cur_path+new_dir_name+"\\"+new_dir_name+'.txt'
        imgsrcs=cur_path+new_dir_name+"\\"+'src.txt'
       
        sel=Selector(response)
       
        #处理标题
        title=sel.xpath('body/table[3]/tr[2]/td[1]/table[1]/tr[1]/td[1]/text()').extract()[-1].strip()
        #处理日期
        s="来源:添加时间:".decode('utf-8')
        date=sel.xpath('body/table[3]/tr[2]/td[1]/table[1]/tr[3]/td[1]/text()').extract()[0].decode('utf-8').replace(s,"")
        #处理正文
        content=sel.xpath('body/table[3]/tr[2]/td[1]/table[1]/tr[5]/td[1]/*')
        #摘取图片链接
        srcs=content.css("img[src]")
        ff=open(filename,'wb')
        fi=open(imgsrcs,'wb')
       
        #写入文件
        for src in srcs:
            img_sel=Selector(text=src.extract(),type="html")
            imgurl="http://eic.jnu.edu.cn"+img_sel.xpath("//@src").extract()[0]
            fi.write(imgurl+'\r\n')
            try:
                out_put_path=img_path+'\\'+imgurl.split('/')[-1]
                thread.start_new_thread(downloadimg,(imgurl,out_put_path))
            except:
                pass
            
        fi.close()

        ff.write("title: "+title+'\r\n')
        ff.write("date: "+date+'\r\n')
        ff.write("content: \r\n")
        for text in content:
            ff.write(text.extract())
        
        ff.write('\r\n')
        ff.close()
       

其中读取url是读取刚刚抓取到的url,我把他们写到同一个文件了. 看到这里的xpath多折磨人了吧,旧站的开发者你出来,我保证不打死你- -

3.注意事项

xpath

	response.xpath('//div[@id="main_content"]').extract()
	#提取结果:"<div id="main_content"><p>测试文本</p></div>"
	
	response.xpath('//div[@id="main_content"]/*').extract()
	#提取结果:"<p>测试文本</p>,某些情况也可能提取不完整
	
	response.xpath('//div[@id="main_content"]/node()').extract()
	#同上,但更保险

css选择器

除了xpath外,Scrapy还提供CSS风格的选择器,于是可以像CSS和jQuery那样选择元素,对于结构明确的html文本用起来还是很爽的,官方教程和下面的例子可以感受一下:

from scrapy import Selector
doc = """
    <div>
       <ul>
             <li id="theid" class="item-0"><a href="link1.html">first item</a></li>
             <li class="item-1"><a href="link2.html">second item</a></li>
             <li class="item-inactive"><a href="link3.html">third item</a></li>
             <li class="item-1"><a href="link4.html">fourth item</a></li>
             <li class="item-0"><a href="link5.html">fifth item</a></li>
        </ul>
     </div>
    """
sel = Selector(text=doc, type="html")
print(sel.css('#theid').extract())
print(sel.css('#theid::attr(class)').extract())
print(sel.css('a[href]').extract())
print(sel.css('a::text').extract())

中文字符串

# -*- coding: utf-8 -*- 

否则无论注释还是字符串都不给用中文.

s="来源:添加时间:".decode('utf-8')
date=sel.xpath('body/td[1]/text()').extract()[0].decode('utf-8').replace(s,"")
#把"来源:添加时间:"删掉

下载图片

直接用urllib2模块的urlopen方法打开图片的URL,然后读出来,写到指定的文件中去就行了:

import urllib2
#下载图片
def downloadimg(url,out_put_path):
    socket=urllib2.urlopen(url)
    data=socket.read()
    with open(out_put_path,'wb') as jpg:
        jpg.write(data)
    socket.close()

配合多线程效果更佳:

import thread
try:
    thread.start_new_thread(downloadimg,(imgurl,out_put_path))
except:
    pass

小结

到这里基本上是成功了,需要的就是建立好文件结构,把抓回来的数据存起来就好了,学生工作,活动之类的都可以用相同的方法写出来,最后的代码和抓取的数据都在我的github上,有兴趣的可以翻一翻: https://github.com/DengZuoheng/pyspider4twxsh

参考文献