项目 用Scrapy写的定向爬虫,旨在通过的discuz的抓取论坛

heartsong/scrapy.cfg文件

#由scrapy startproject自动创建
#
#有关[部署]部分的更多信息,请参阅:
#https ://scrapyd.readthedocs.org/en/latest/deploy.html

[设置]
默认 = heartsong.settings

[部署]
# URL = HTTP://本地主机:6800 /
project = heartsong

heartsong/heartsong/items.py文件

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

import scrapy

class HeartsongItem(scrapy。项):
    title = scrapy.Field()#帖子   的标题
    url = scrapy.Field()#帖子   的网页链接
    author = scrapy.Field()#帖子   的作者
    post_time = scrapy.Field()   #发表时间
    content = scrapy.Field()#帖子   的内容

heartsong/heartsong/pipelines.py文件

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

class HeartsongPipeline(object):
    def  process_item(self,item,spider):
        file  =  open(“ items.txt ”,“ a ”)   #以追加的方式打开文件,不存在则创建
        #因为项目中的数据是unicode的编码,为了在控制台中查看数据的有效性和保存,
        #将其编码改为UTF-8
        item_string =  str(item).decode(“ unicode_escape ”).encode(' utf-8 ')
        file .write(item_string)
        file .write(' \ n ')
        文件 .close()
        print item_string   #在控制台输出
        return item   #会在控制台输出原料数据,可以选择不写

heartsong/heartsong/settings.py文件

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

BOT_NAME  =  ' heartsong '

SPIDER_MODULES  = [ ' heartsong.spiders ' ]
NEWSPIDER_MODULE  =  ' heartsong.spiders '

ROBOTSTXT_OBEY  =  False   #不遵守Robot协议

#配置管道,数字代表优先级,因为本项目只有一个管道,所以可取1-1000中的任意值
ITEM_PIPELINES  = {
    ' heartsong.pipelines.HeartsongPipeline ': 300,
}

heartsong/heartsong/spiders/heartsong_scrapy.py文件

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

#进口scrapy#可以用这句代替下面三句,但不推荐
from scrapy.spiders import spiders
from scrapy.selector import Selector
[from scrapy] import request
from heartsong.items import HeartsongItem   #如果报错是pyCharm对目录理解错误的原因,不影响

class HeartsongSpider(spiders):
    name =  “ heartsong ”
    allowed_domains = [ “ heartsong.top ” ]   #允许爬取的域名,非此域名的网页不会取取
    start_urls = [
        #起始URL,这里设置为从最大TID开始,向0的方向迭代
        “ http://www.heartsong.top/forum.php?mod=viewthread&tid=34 ”
    ]

    #用来保持登录状态,可把铬上拷贝下来的字符串形式的cookie转化成字典形式,粘贴到此处
    cookies = {}

    #发送给服务器的HTTP头信息,有的网站需要伪装出浏览器头进行爬取,有的则不需要
    头= {
        # '连线':'保持活力',
        ' User-Agent ': ' Mozilla / 5.0(X11; Linux x86_64)AppleWebKit / 537.36(KHTML,如Gecko)Chrome / 52.0.2743.82 Safari / 537.36 '
    }

    #对请求的返回进行处理的配置
    meta = {
        ' dont_redirect ': True,  #禁止网页重定向
        ' handle_httpstatus_list ':[ 301, 302 ]  #对哪些异常返回进行处理
    }

    def  get_next_url(self,oldUrl):
        “””
        说明:返回下次迭代的网址
        :param oldUrl:上一个爬去过的url
        :return:下次要爬取的url
        “””
        #传入的URL格式:HTTP://www.heartsong.top/forum.php MOD = viewthread&TID = 34
        l = oldUrl.split(' = ')   #用等号分割字符串
        oldID =  int(l [ 2 ])
        newID = oldID -  1
        if newID ==  0:   #如果tid迭代到0了,说明网站爬完,爬虫可以结束了
            返回
        newUrl = l [ 0 ] +  “ = ”  + l [ 1 ] +  “ = ”  +  str(newID)   #构造出新的url
        return  str(newUrl)   #返回新的url

    def  start_requests(self):
        “””
        这是一个重载函数,它的作用是发出第一个请求请求
        :返回:
        “””
        #带着头,cookie去请求self.start_urls [0],返回的响应会被送到
        #回调函数解析中
        yield Request(self .start_urls [ 0 ],
                             callback = self .parse,headers = self .headers,
                             cookies = self .cookies,meta = self .meta)

    def  parse(self,response):
        “””
        用以处理主题贴的首页
        :参数响应:
        :返回:
        “””
        选择器=选择器(响应)   #创建选择器

        table = selector.xpath(' // * [starts-with(@id,“pid”)] ')   #取出所有的楼层
        如果 没有表格:
            #这个链接内没有一个楼层,说明此主题贴可能被删了,
            #把这类网址保存到一个文件里,以便审查原因
            打印 “不良网址!”
            f =  开放(' badurl.txt ',' a ')
            f.write(response.url)
            f.write(' \ n ')
            f.close()
            #发起下一个主题贴的请求
            next_url =  self .get_next_url(response.url)   # response.url就是原请求的url
            如果 next_url !=  None:   #如果返回了新的url
                yield请求(next_url,callback = self .parse,headers = self .headers,
                                cookies = self .cookies,meta = self .meta)
            返回
        对于每一个在表:
            item = HeartsongItem()   #实例化一个item
            #因为后来我在论坛里删除了大量的机器回帖,所以有的楼层里没有作者信息
            尝试:
                #通过XPath的匹配信息,注意提取物()方法返回的是一个列表
                item [ ' author ' ] = each.xpath(' tr [1] / td [@ class =“pls”] / div [@ class =“pls favatar”] / div [@ class =“pi”] / div [ @ class =“authi”] / a / text()').extract()[ 0 ]
                item [ ' post_time ' ] = each.xpath(' tr [1] / td [@ class =“plc”] / div [@ class =“pi”] ').re(r ' [ 0-9 ] + - [ 0-9 ] + -  [ 0-9 ] + [ 0-9 ] +:[ 0-9 ] +:[ 0-9 ] + ')[ 0 ]
            除了:
                继续
            #的XPath的字符串(。)用法,解决标签套标签的情况,具体解释请自行找的XPath教程
            content_list = each.xpath(' .// td [ @class = " t_f“] ').xpath(' string(。)').extract()
            content =  “ ”. join(content_list)   #将列表转化为字符串
            item [ ' url ' ] = response.url   #用这种方式获取网页的网址
            #把内容中的换行符,空格等去掉
            item [ ' content ' ] = content.replace(' \ r \ n ',' ').replace('  ',' ').replace(' \ n ',' ')
            yield item   #将创建并赋值好的Item对象传递到PipeLine当前进行处理

        pages = selector.xpath(' // * [@ id =“pgt”] / div / div / label / span ')
        如果页面:   #如果页面不是空列表,说明该主题帖分页
            pages = pages [ 0 ] .re(r ' [ 0-9 ] + ')[ 0 ]   #正则匹配出总页数
            打印 “这篇文章有”,页面,“页面”
            # response.url格式:http://www.heartsong.top/forum.php?mod=viewthread&tid=34
            #子utl格式:http://www.heartsong.top/forum.php?mod=viewthread&tid=34&page=1
            tmp = response.url.split(' = ')   #以下 =分割网址
            #循环生成所有子页面的请求
            用于 PAGE_NUM 在 x范围(2,INT(页)+  1):
                #构造新的网址
                sub_url = tmp [ 0 ] +  ' = '  + tmp [ 1 ] +  ' = '  + tmp [ 2 ] +  ' page = '  +  str(page_num)
                #注意此处的回调函数是self.sub_parse,就是说这个请求的响应会传到
                # self.sub_parse里去处理
                yield Request(sub_url,callback = self .sub_parse,headers = self .headers,
                                cookies = self .cookies,meta = self .meta)

        #发起下一个主题贴的请求
        next_url =  self .get_next_url(response.url)   # response.url就是原请求的url
        如果 next_url !=  None:   #如果返回了新的url
            yield请求(next_url,callback = self .parse,headers = self .headers,
                        cookies = self .cookies,meta = self .meta)

    def  sub_parse(self,response):
        “””
        用以爬取主题贴除首页外的其他子页
        :参数响应:
        :返回:
        “””
        选择器=选择器(响应)
        table = selector.xpath(' // * [starts-with(@id,“pid”)] ')   #取出所有的楼层
        对于每一个在表:
            item = HeartsongItem()   #实例化一个item
            尝试:
                #通过XPath的匹配信息,注意提取物()方法返回的是一个列表
                item [ ' author ' ] = each.xpath(' tr [1] / td [@ class =“pls”] / div [@ class =“pls favatar”] / div [@ class =“pi”] / div [ @ class =“authi”] / a / text()').extract()[ 0 ]
                item [ ' post_time ' ] = each.xpath(' tr [1] / td [@ class =“plc”] / div [@ class =“pi”] ').re(r ' [ 0-9 ] + - [ 0-9 ] + -  [ 0-9 ] + [ 0-9 ] +:[ 0-9 ] +:[ 0-9 ] + ')[ 0 ]
            除了:
                继续
            content_list = each.xpath(' .// td [ @class = " t_f“] ').xpath(' string(。)').extract()
            content =  “ ”. join(content_list)   #将列表转化为字符串
            item [ ' url ' ] = response.url   #用这种方式获取网页的网址
            #把内容中的换行符,空格等去掉
            item [ ' content ' ] = content.replace(' \ r \ n ',' ').replace('  ',' ').replace(' \ n ',' ')
            yield item   #将创建并赋值好的Item对象传递到PipeLine当前进行处理

猜你喜欢

转载自blog.csdn.net/fengxueersui/article/details/80054466