scrapy 搜索关键字_python scrapy 入门爬虫 「什么值得买」关键字搜索

安装scrapy框架

pip install Scrapy

创建一个scrapy工程,名字为smzdm

scrapy startproject smzdm

创建包含下列内容的 smzdm 目录:

smzdm/

scrapy.cfg

smzdm/

__init__.py

items.py

pipelines.py

settings.py

spiders/

__init__.py

...

这些文件分别是:

scrapy.cfg: 项目的配置文件

smzdm/: 该项目的python模块。之后您将在此加入代码。

smzdm/items.py: 项目中的item文件.

smzdm/pipelines.py: 项目中的pipelines文件.

smzdm/settings.py: 项目的设置文件.

smzdm/spiders/: 放置spider代码的目录.

进入工程目录,使用以下命令创建一个爬虫(Spider)文件

scrapy genspider concrete_search search.smzdm.com

将会spiders目录下,生成name='concrete_search'    start_urls='search.smzdm.com'   的 concrete_search.py文件

import scrapy

class ConcreteSearchSpider(scrapy.Spider):

name = 'concrete_search'

allowed_domains = ['search.smzdm.com/']

start_urls = ['http://search.smzdm.com/']

def parse(self, response):

pass

文件创建了一个ConcreteSearchSpider继承 scrapy.Spider 类, 且定义以下三个属性:

name: 用于区别Spider。 该名字必须是唯一的,您不可以为不同的Spider设定相同的名字。

start_urls: 包含了Spider在启动时进行爬取的url列表。 因此,第一个被获取到的页面将是其中之一。 后续的URL则从初始的URL获取到的数据中提取。

parse() 是spider的一个方法。 被调用时,每个初始URL完成下载后生成的 Response 对象将会作为唯一的参数传递给该函数。 该方法负责解析返回的数据(response data),提取数据(生成item)以及生成需要进一步处理的URL的 Request 对象。

在items.py文件中,定义出需要采集的数据实体

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

import scrapy

class SmzdmItem(scrapy.Item):

id = scrapy.Field() #id

title = scrapy.Field() #标题

price = scrapy.Field() #价格

desc = scrapy.Field() #描述

zhi_yes = scrapy.Field() #点「值」的数量

zhi_no = scrapy.Field() #点「不值」的数量

praise = scrapy.Field() #文章「赞」的数量

start = scrapy.Field() #「收藏」的数量

comment = scrapy.Field() #「评论」的数量

time = scrapy.Field() #发布时间

channel = scrapy.Field() #购买渠道

detail_url = scrapy.Field() #详情页面url

url = scrapy.Field() #商品链接

img = scrapy.Field() #商品图片

def __str__(self):

return 'SmzdmItem (%s) (%s) (值:%s) (%s)' % (self['title'], self['price'], self['start'], self['detail_url'])

__repr__ = __str__

这里我定义了一个SmzdmItem,继承了scrapy.Item。并且重写了__str__和__repr__,方便打印时能比较方便看到信息。

解析网页

在spider文件中的parse方法里,一般使用xpath、css来解析html,从某个具体的元素中取值。

一个例子如下

def parse(self, response):

sites = response.css('#site-list-content > div.site-item > div.title-and-desc')

items = []

for site in sites:

item = Website()

item['name'] = site.css(

'a > div.site-title::text').extract_first().strip()

item['url'] = site.xpath(

'a/@href').extract_first().strip()

item['description'] = site.css(

'div.site-descr::text').extract_first().strip()

items.append(item)

在chrome里可以这样获取到xpath

a855b8e8394d3a4c482110e138be8c3c.png

关闭『遵循robot协议』

大部分网站都在robot协议中(比如https://www.smzdm.com/robots.txt)禁止了内容的爬去

scrapy默认遵守了网站的这个robot协议。

所以为了成功爬取内容,需要在settings.py中,把ROBOTSTXT_OBEY置为False

ROBOTSTXT_OBEY = False

使用pipelines.py进行筛选

用于去重或者筛选item

from scrapy.exceptions import DropItem

class SmzdmPipeline(object):

# 3.过滤条件

# 3.1表示商品的『值』数量必须 >= zhi_yes_limit

zhi_yes_limit = -1

# 3.2表示商品的『不值』数量必须 <= zhi_no_limit

zhi_no_limit = -1

# 3.3表示商品的『值』除以『不值』的比率必须 >= zhi_no_limit

zhi_ratio_limit = -1

# 3.4表示商品的『收藏』数量必须 >= start_limit

start_limit = -1

# 3.5表示商品的『评论』数量必须 >= comment_limit

comment_limit = -1

# 3.6需要排除的关键字

exclude = []

def __init__(self):

self.ids_seen = set()

def process_item(self, item, spider):

if item['id'] in self.ids_seen:

raise DropItem("Duplicate item found: %s" % item)

else:

self.ids_seen.add(item['id'])

if SmzdmPipeline.zhi_yes_limit > -1 and item['zhi_yes'] < SmzdmPipeline.zhi_yes_limit:

raise DropItem("zhi_yes_limit: %d, target: %s" % (SmzdmPipeline.zhi_yes_limit, str(item)))

if SmzdmPipeline.zhi_no_limit > -1 and item['zhi_no'] > SmzdmPipeline.zhi_no_limit:

raise DropItem("zhi_no_limit: %d, target: %s" % (SmzdmPipeline.zhi_no_limit, item))

if SmzdmPipeline.zhi_ratio_limit > -1 and (item['zhi_yes'] / item['zhi_no']) > SmzdmPipeline.zhi_ratio_limit:

raise DropItem("zhi_ratio_limit: %d, target: %s" % (SmzdmPipeline.zhi_ratio_limit, item))

if SmzdmPipeline.start_limit > -1 and item['zhi_start'] < SmzdmPipeline.zhi_start_limit:

raise DropItem("zhi_start_limit: %d, target: %s" % (SmzdmPipeline.zhi_start_limit, item))

if SmzdmPipeline.comment_limit > -1 and item['zhi_commen'] <= SmzdmPipeline.comment_limit:

raise DropItem("zhi_comment_limit: %d, target: %s" % (SmzdmPipeline.zhi_comment_limit, item))

if len(SmzdmPipeline.exclude) > 0 and SmzdmPipeline.containsKeyword(item['title']):

raise DropItem("exclude: %s, target: %s" % (str(SmzdmPipeline.exclude), item))

print("符合条件:" + str(item))

return item

@classmethod

def containsKeyword(cls, title):

for keyword in cls.exclude:

if keyword in title:

return True

return False

这里我写了一个,里面有去重的代码,还有根据点值人数,收藏人数,点值/点不值比率筛选的逻辑

写完后需要到settings.py开启这个功能

ITEM_PIPELINES = {

'smzdm.pipelines.SmzdmPipeline': 300,

}

分配给每个类的整型值,确定了他们运行的顺序,item按数字从低到高的顺序,通过pipeline,通常将这些数字定义在0-1000范围内。

启动爬虫

进入项目的根目录,运行下列命令启动爬虫:

scrapy crawl concrete_search

为了方便,我在 concrete_search.py 加入了以下代码

if __name__ == "__main__":

name = 'concrete_search'

cmd = 'scrapy crawl {0}'.format(name)

cmdline.execute(cmd.split())

这样直接运行该文件就可以启动爬虫了。

============================================================================

以下是我写的爬虫的代码,筛选条件在__init__方法中可以修改

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

from urllib import parse

import scrapy

from scrapy import cmdline

from smzdm.items import SmzdmItem

from smzdm.pipelines import SmzdmPipeline

class ConcreteSearchSpider(scrapy.Spider):

# 爬虫的名字

name = 'concrete_search'

# value:SmzdmItem

SmzdmItemList = []

def __init__(self):

# 1.搜索的商品名称

feed = ['奶粉']

# 2.需要搜索的页数,一页为20个商品

pages = 5

# 3.过滤条件

# 3.1表示商品的『值』数量必须 >= zhi_yes_limit

zhi_yes_limit = 5

# 3.2表示商品的『不值』数量必须 <= zhi_no_limit

zhi_no_limit = -1

# 3.3表示商品的『值』除以『不值』的比率必须 >= zhi_no_limit

zhi_ratio_limit = -1

# 3.4表示商品的『收藏』数量必须 >= start_limit

start_limit = -1

# 3.5表示商品的『评论』数量必须 >= comment_limit

comment_limit = -1

# 3.6需要排除的关键字

exclude = ['婴儿', '幼儿', '宝宝']

SmzdmPipeline.zhi_yes_limit = zhi_yes_limit

SmzdmPipeline.zhi_no_limit = zhi_no_limit

SmzdmPipeline.zhi_ratio_limit = zhi_ratio_limit

SmzdmPipeline.start_limit = start_limit

SmzdmPipeline.comment_limit = comment_limit

SmzdmPipeline.exclude = exclude

for index, f in enumerate(feed):

feed[index] = parse.quote(f)

self.start_urls = ['http://search.smzdm.com/?c=home&s=%s&p=%d&v=a' % (y, x) for x in range(1, pages + 1) for y

in feed]

def parse(self, response):

selector_feed_main_list = response.selector.xpath('//*[@id="feed-main-list"]')[0]

selector_list_feed_main_list = selector_feed_main_list.xpath('./li')

for selector in selector_list_feed_main_list:

selector_item = selector.xpath('./div/div[2]')

# 过滤文章

if len(selector_item.xpath('./div[2]/div[2]/div/div/a')) == 0 or len(

selector_item.xpath('./h5/a[2]/div/text()')) == 0:

print(

"发现一篇资讯 : 获取%s个赞,名字如下" % selector_item.xpath('./div[2]/div[1]/span[1]/span[1]/text()')[0].extract())

print(selector_item.xpath('./h5/a[1]/text()')[0].extract())

continue

item = SmzdmItem()

item['id'] = int(selector_item.xpath('./div[2]/div[1]/span[1]/span[1]/@data-article')[0].extract())

item['title'] = selector_item.xpath('./h5/a[1]/text()')[0].extract().strip()

if len(selector_item.xpath('./h5/a[2]/div/text()')) != 0:

item['price'] = selector_item.xpath('./h5/a[2]/div/text()')[0].extract()

desc_text_count = len(selector_item.xpath('./div[1]/text()').extract())

if desc_text_count == 1 or selector_item.xpath('./div[1]/text()').extract()[0].strip() != '':

item['desc'] = selector_item.xpath('./div[1]/text()')[0].extract().strip()

elif desc_text_count >= 2 and selector_item.xpath('./div[1]/text()').extract()[1].strip() != '':

item['desc'] = selector_item.xpath('./div[1]/text()')[1].extract().strip()

if len(selector_item.xpath('./div[2]/div[1]/span[1]/span[1]/span[1]/span/text()')) != 0:

item['zhi_yes'] = int(selector_item.xpath('./div[2]/div[1]/span[1]/span[1]/span[1]/span/text()')[

0].extract())

item['zhi_no'] = int(selector_item.xpath('./div[2]/div[1]/span[1]/span[2]/span[1]/span/text()')[0].extract())

item['start'] = int(selector_item.xpath('./div[2]/div[1]/span[2]/span/text()')[0].extract())

# 待优化 item['comment'] = selector.xpath('./div[2]/div[1]/a/text()')[0].extract()

item['comment'] = int(selector_item.xpath('./div[2]/div[1]/a/@title')[0].extract().split(' ')[1])

item['time'] = selector_item.xpath('./div[2]/div[2]/span/text()')[0].extract().strip()

item['channel'] = selector_item.xpath('./div[2]/div[2]/span/span/text()')[0].extract().strip()

item['detail_url'] = selector.xpath('./div/div[1]/a/@href')[0].extract().strip()

item['url'] = selector_item.xpath('./div[2]/div[2]/div/div/a/@href')[0].extract()

item['img'] = selector.xpath('./div/div[1]/a/img/@src')[0].extract()

# print(item)

ConcreteSearchSpider.SmzdmItemList.append(item)

yield item

# print(ConcreteSearchSpider.SmzdmItemList)

if __name__ == "__main__":

name = 'concrete_search'

cmd = 'scrapy crawl {0}'.format(name)

cmdline.execute(cmd.split())

项目连接:https://github.com/yaodiwei/smzdm

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值