scrapy爬虫(以东方烟草网为例)

一、相对网址转绝对网址
项目需要爬取东方烟草网的要闻速递板块的新闻,使用Firebug检查发现,//tr[@id="titleList"]//td[@class="sj_text1"]/a/@href中的网址是相对网址,无法直接访问,需要转成绝对网址才能进一步爬取该链接对应的内容。

在网上搜索发现很多相关介绍,出现频率最高的是urljoin_rfc函数,但我在我的pycharm中输这个函数会出现如下效果:

最后决定用urljoin函数把两段字符串形式的url拼接在一起。
又因为相对网址前面有../../这种,所以使用.split()函数切片取../后面的内容。

base_url_1 = 'http://www.eastobacco.com/zxbk/'
for i in range(0,len(item['temp_1'])):
    temp = ''
    relative_url = item['temp_1'][i].encode("utf-8")
    temp = urljoin(base_url_1, relative_url.split('../')[-1])

但此时仍然会出现raiseValueError(‘Missing scheme in request url:%s’% self._url),再次检查发现,不同的相对地址的前缀还不一样,于是观察修改,分别给不同的相对地址添加上不同的前缀:

base_url_1 = 'http://www.eastobacco.com/zxbk/'
        base_url_2 = 'http://www.eastobacco.com/zxbk/dyzxzx/ywsd/'
        base_url_3 = 'http://www.eastobacco.com/zxbk/dyzxzx/'
        base_url_4 = 'http://www.eastobacco.com/'
        item['temp'] = []

        j = 0
        for i in range(0,len(item['temp_1'])):

            temp = ''
            relative_url = item['temp_1'][i].encode("utf-8")
            if relative_url.startswith('../../gjjdzcybdj'):   #gjjdzcybdj开头的,前面部分一直添加到zxbk
                temp = urljoin(base_url_1, relative_url.split('../')[-1])
                temp = temp.decode("utf-8")
            if relative_url.startswith('./20'):   #数字开头的,前面部分添加到ywsd
                temp = urljoin(base_url_2, relative_url)
                temp = temp.decode("utf-8")
            if relative_url.startswith('../gjjwj/20'):   #gjjwj开头的,前面部分添加到dyzxzx/
                temp = urljoin(base_url_3, relative_url.split('../')[-1])
                temp = temp.decode("utf-8")
            if relative_url.startswith('../../../spyl'):  # gjjdzcybdj开头的,前面部分一直添加到zxbk
                temp = urljoin(base_url_4, relative_url.split('../')[-1])
                temp = temp.decode("utf-8")
            if (j % 2) == 0:
                item['temp'].append(temp)
            j = j + 1  # 每个标签里有两个重复的href

之所以要隔一个在item[‘temp’]里面append一个temp,是因为每个标题对应两个重复的href。

二、item中不同的key对应list的个数不同
放在东方烟草网这个例子中,就是说本来应该一个标题对应一个链接和一个日期的,但爬下来发现新闻标题数目和链接数目及日期数目不一致。
检查xpath后发现第一页的新闻标题中出现了一个生僻字:
这个搜狗输入法都打不出来的字(==)导致这一个标题对应了两个list元素

于是只好去重那个多出来的list元素。

三、爬下来的内容出现重复和遗漏
检查无误后运行爬虫,发现明明return的item中内容完整没有遗漏,但到了pipeline中的process_item方法里,item中就出现了重复的内容,内容总数一定,所以就造成了一些内容的遗漏,百思不得其解,最后在settings.py中加上

DOWNLOAD_DELAY = 3
DOWNLOAD_HANDLERS = {'s3': None}

减慢了爬虫速度后,内容终于正常。

四、完整代码及注释

test.py

# -*- coding: utf-8 -*-
#东方烟草网 要闻速递
__author__ = 'leilu'

import scrapy
from scrapy.spiders import Spider
from scrapy.selector import Selector
from testproject.items import TestprojectItem
from scrapy.http import Request
from scrapy.utils.response import get_base_url
from scrapy.utils.url import urljoin_rfc
from urlparse import urljoin
import sys
reload(sys)
sys.setdefaultencoding("utf-8")

class DmozSpider(Spider):
    name = "eastobacco"

    start_urls = [
        'http://www.eastobacco.com/zxbk/dyzxzx/ywsd/'  #要闻速递
    ]
    start_urls = []
    for i in range(1, 5):       #1-4
        url = 'http://www.eastobacco.com/zxbk/dyzxzx/ywsd/index_' \
              + str(i) + '.html'
        start_urls.append(url)

    def parse(self, response):

        sel = Selector(response)  # Xpath选择器
        item = TestprojectItem()
        item['temp1'] = sel.xpath('//tr[@id="titleList"]//td[@class="sj_text1"]/a/text()').extract()   #题目
        item['temp_1'] = sel.xpath('//tr[@id="titleList"]//td[@class="sj_text1"]/a/@href').extract()        #从start_urls中分析出的一个网址赋值给url
        item['temp2'] = sel.xpath('//td[@class="sj_hui"]/text()').extract()      #日期
        #relative_url = sel.xpath('//td[@class="sj_text1"]/a/@href').extract()
        base_url_1 = 'http://www.eastobacco.com/zxbk/'
        base_url_2 = 'http://www.eastobacco.com/zxbk/dyzxzx/ywsd/'
        base_url_3 = 'http://www.eastobacco.com/zxbk/dyzxzx/'
        base_url_4 = 'http://www.eastobacco.com/'
        item['temp'] = []

        j = 0
        for i in range(0,len(item['temp_1'])):

            temp = ''
            relative_url = item['temp_1'][i].encode("utf-8")
            if relative_url.startswith('../../gjjdzcybdj'):   #gjjdzcybdj开头的,前面部分一直添加到zxbk
                temp = urljoin(base_url_1, relative_url.split('../')[-1])
                temp = temp.decode("utf-8")
            if relative_url.startswith('./20'):   #数字开头的,前面部分添加到ywsd
                temp = urljoin(base_url_2, relative_url)
                temp = temp.decode("utf-8")
            if relative_url.startswith('../gjjwj/20'):   #gjjwj开头的,前面部分添加到dyzxzx/
                temp = urljoin(base_url_3, relative_url.split('../')[-1])
                temp = temp.decode("utf-8")
            if relative_url.startswith('../../../spyl'):  # gjjdzcybdj开头的,前面部分一直添加到zxbk
                temp = urljoin(base_url_4, relative_url.split('../')[-1])
                temp = temp.decode("utf-8")
            if (j % 2) == 0:
                item['temp'].append(temp)
            j = j + 1  # 每个标签里有两个重复的href

        #item['temp1'] = item['temp1'][:16]+item['temp1'][17:]   #第一页的标题中有一个生僻字导致标题数量比链接和日期数多一个
        print len(item['temp1'])


        for url in item['temp']:
            #print url
            yield Request(url, meta={'item': item},
                    callback=self.parse_content)

    def parse_content(self, response):
        item = response.meta['item']

        #print item['temp1']
        num = item['temp'].index(response.url)       #list.index(obj)返回找到对象的索引
        item['title'] = item['temp1'][num]
        #print item['title']
        item['link'] = item['temp'][num]   #即等于response.url
        #print item['link']
        item['date'] = item['temp2'][num]
        #print item['date']
        print '---第%s条---'%num
        content1 = [
            '//div[@class="TRS_Editor"]//p//text()',
        ]

        for i in range(0, len(content1)):
            if response.xpath(content1[i]).extract() != []:
                item['content'] = response.xpath(content1[i]).extract()
                break
            else:
                item['content'] = ['empty']
        content = ""  # 内容
        for i in range(len(item['content'])):
            content += item['content'][i]
            content = "".join(content.split())
            content = content.replace("'", "\"")
        #print content
        item['contentnew'] = []
        item['contentnew'].append(content)
        #print item['contentnew']

        return item

pipeline.py可根据需要自己保存到数据库或Excel表,没什么难度,就不放了。

你可能感兴趣的:(python,scrapy)