Python分布式爬虫:scrapy爬取单个网页

明确爬取网站:<a>http://blog.jobbole.com/</a>
抓取策略:按照所有文章的分页,逐页抓取。
具体策略一:更改页码值http://blog.jobbole.com/all-posts/page/8/
弊端:总页数发生变化的时候,需要修改源码
具体策略二:逐步提取下一页,随着页面发生改变也不用修改源码
下面使用的是策略二。

准备工作:
新建虚拟环境:

C:\Users\wex>mkvirtualenv article
Using base prefix 'c:\\users\\wex\\appdata\\local\\programs\\python\\python35'
New python executable in C:\Users\wex\Envs\article\Scripts\python.exe
Installing setuptools, pip, wheel...done.

创建项目:

I:\python项目>scrapy startproject ArticleSpider
New Scrapy project 'ArticleSpider', using template directory 'c:\\users\\wex\\appdata\\local\\programs\\python\\python35\\lib\\site-packages\\scrapy\\templates\\project', created in:
    I:\python项目\ArticleSpider

You can start your first spider with:
    cd ArticleSpider
    scrapy genspider example example.com

根据默认模板创建爬虫文件:

I:\python项目\ArticleSpider>scrapy genspider jobbole blog.jobbole.com
Created spider 'jobbole' using template 'basic' in module:
  ArticleSpider.spiders.jobbole

scrapy启动某个spider的方法(其中的name为spider中的name):

scrapy  crawl jobbole

当报错的时候:

ImportError: No module named 'win32api'

根据报错安装:

I:\python项目\ArticleSpider>pip install   pypiwin32
Collecting pypiwin32
  Downloading pypiwin32-219-cp35-none-win_amd64.whl (8.6MB)
    100% |████████████████████████████████| 8.6MB 88kB/s
Installing collected packages: pypiwin32
Successfully installed pypiwin32-219

这时候再启动不会报错。

在Pycharm中新建main.py文件,用于运行spider文件。

#调用这个函数可以执行scrapy文件
from scrapy.cmdline import execute

import sys
import os

#设置ArticleSpider工程目录
sys.path.append(os.path.dirname(os.path.abspath(__file__)))

#调用execute函数运行spider
execute(["scrapy","crawl","jobbole"])

注意一点,在settings文件中的:

# Obey robots.txt rules
ROBOTSTXT_OBEY = False

防止不符合协议的url过滤掉。

关于xpath:简介,术语,语法

1,xpath使用多路径表达式在xml和html中进行导航
2,xpath包含标准函数库
3,xpath是一个w3c的标准

xpath节点关系
1,父节点
2,子节点
3,兄弟节点
4,先辈节点
5,后代节点

语法:

选取节点

表达式      说明
.      选取当前节点
..     选取当前节点的父节点
@      选取属性

article       选取所有article元素的所有子节点
/article      选取根元素article
article/a    选取所有属于article的子元素的a元素
//div          选取所有div元素(不论出现在文档任何地方)
//@class   选取所有名为class的属性

谓语(Predicates)

谓语用来查找某个特定的节点或者包含某个指定的值的节点。
谓语被嵌在方括号中

/article/div[1]         选取属于article子元素的第一个div元素
/article/div[last()]   选取属于article子元素的最后一个div元素
/article/div[last()-1]      选取属于article子元素的倒数第二个div元素
/article/div[last()<3]     选取最前面的两个article元素的子元素的div元素
//div[@lang]           选取所有拥有lang属性的div元素
//div[@lang='eng']      选取所有lang属性为eng的div元素
/bookstore/book[price>35.00]      选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。
/bookstore/book[price>35.00]/title      选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。

选取未知节点

*      匹配任何元素节点
@*      匹配任何属性节点
node()      匹配任何类型的节点

通过在路径表达式中使用“|”运算符,您可以选取若干个路径。

/div/*         选取属于div元素的所有子节点
//*              选取所有元素
//div[@*]    获取所有有带属性的div元素
/div/a | /div/p   获取所有div元素的a和p元素
//span | //ul     选取文档中的span和ul元素
article/div/p | //span    选取所有属于article元素的div元素的p元素  以及文档中所有的span元素

注意一点:通过F12查看得到的是包括加载了js和css的,和通过查看源代码得到的可能不一致。

我们提取网页的内容:
标题,日期,评论,正文内容

我们调用scrapy shell,可以一次抓取,然后调试。

通过xpath获取一些值:
通过调用scrapy的shell ,可以一次抓取,省得每次抓取都进行一次url请求。

scrapy shell http://blog.jobbole.com/110287/

>>> title =   response.xpath('//*[@id = "post-110287"]/div[1]/h1/text()')
>>> title
[<Selector xpath='//*[@id = "post-110287"]/div[1]/h1/text()' data='2016 腾讯软件开发面试题(部分)'>]
>>> title.extract()
['2016 腾讯软件开发面试题(部分)']
>>> title.extract()[0]
'2016 腾讯软件开发面试题(部分)'

注意:xpath返回值可以再进行xpath选取节点,但是经过extract()之后,就会变成列表。

在Pycharm环境中:

    def parse(self, response):
        #/html/body/div[2]/div[2]/div[1]/div[1]/h1/
        #//*[@id="post-110923"]/div[1]/h1/

        title = response.xpath('//*[@id="post-110923"]/div[1]/h1/text()').extract()[0]
        #通过ID准确定位
        #re_selector = response.xpath("/html/body/div[2]/div[2]/div[1]/div[1]/h1/)
        #之所以没值,是因为有额度div是加载过js生成的
        #re3_selector = response.xpath('//div[@class="entry-header"]/h1/text()')
        #这里通过class定位


        create_date = response.xpath('//p[@class="entry-meta-hide-on-mobile"]/text()').extract()[0].strip().replace("·","").strip()
        praise_num  = response.xpath('//span[contains(@class,"vote-post-up")]/h10/text()').extract()[0]
        collect_num = response.xpath('//span[contains(@class,"bookmark-btn")]/text()').extract()[0]
        math_re = re.match(".*(\d+).*",collect_num)
        if math_re:
            collect_num = math_re.group(1)
        # comment_num = response.xpath('//*[@id="post-110923"]/div[3]/div[3]/a/').extract()
        #评论数为空

        content = response.xpath('//div[@class="entry"]').extract()[0]
        tag_list= response.xpath('//*[@id="post-110923"]/div[2]/p/a/text()').extract()
        tags    = ','.join(tag_list)

CSS选择器

表达式        说明
*              选取所有节点
#container    选择id为container的节点
.container    选择所有class包含container的节点
li  a      选择所有li下的所有a节点
ul + p    选择ul后面的第一个p元素(兄弟节点)
div#container > ul    选择id为container的div的第一个ul子元素

ul ~ p    选取与ul相邻的所有p元素
a[title]    选取所有有title属性的a元素
a[href="http://jobbole.com"]    选取所有href属性为jobbole.com值的a元素
a[href*="jobbole"]    选取所有href属性包含jobbole的a元素
a[href^="http"]    选取所有href属性值以http开头的a元素
a[href$=".jpg"]    选取所有href属性值以.jpg结尾的a元素
input[type=radio]:checked    选择选中的radio的元素

div:not(#container)    选取所有id非container的div属性
li:nth-child(3)    选取第3个li元素
tr:nth-child(2n)    第偶数个tr
scrapy shell http://blog.jobbole.com/110923/

在此环境下进行

        #通过css选择器
        title = response.css(".entry-header h1::text").extract()
        create_date = response.css("p.entry-meta-hide-on-mobile::text").extract()[0].strip().replace("·","").strip()
        praise_num = response.css(".post-adds h10::text").extract()[0]
        collect_num = response.css(".bookmark-btn::text").extract()
        comment_num = response.css("a[href='#article-comment'] span::text").extract()
        content  =   response.css("div.entry").extract()
        tag_list  =  response.css("p.entry-meta-hide-on-mobile a::text").extract()

注意:
当我们明确结果是一个列表,不确定是否存在值,担心取值报错出现异常的时候,可以使用:

In [14]: response.css("p.entry-meta-hide-on-mobile a::text").extract_first("")
Out[14]: 'IT技术'

函数的默认返回值为None。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354

推荐阅读更多精彩内容