接上篇文章:https://blog.csdn.net/weixin_43094046/article/details/107930745
还是新创建一个项目suningDemo。其实可以在上次的项目中进行迭代这里为了以后方便查看就新建了个项目。
首先将上次的项目整个拷贝。
这次爬取的页面:https://book.suning.com/
页面结构:
这里实现三级页面仅对三个文件suingDemoSpider,Middleware,items进行改动就可实现。
困难点:大小分类的对应。
项目结构:
items加入三级页面所需要的注释
import scrapy
class SuningdemoItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
id = scrapy.Field() # 出版时间
bkName = scrapy.Field() # 图书的名字
price = scrapy.Field() # 图书的价格
commentNumber = scrapy.Field() # 评价的人数
bkShop = scrapy.Field() # 所在的书店
bkUrl = scrapy.Field() # URL
author = scrapy.Field() # 作者
press = scrapy.Field() # 出版社
publishTime = scrapy.Field() # 出版时间
bkType = scrapy.Field() #图书的类别
bkHref = scrapy.Field() #类型的url
bkTypes = scrapy.Field() #图书的小类别
suningDemoSpider
from copy import deepcopy
import scrapy
class SuningdemospiderSpider(scrapy.Spider):
name = 'suningDemoSpider'
allowed_domains = ['suning.com']
start_urls = ['https://book.suning.com/']
# start_urls = ['https://list.suning.com/1-502320-0.html']
def parse(self, response):
book_type = response.xpath('//div[@class="menu-item"]/dl/dt')
item = {}
for book in book_type:
item['bkType'] = book.xpath('./h3/a/text()').extract_first()
book_href = book.xpath('./following-sibling::* /a')#识别大小分类,进行循环输出
for bk in book_href:
item['bkTypes'] = bk.xpath('./text()').extract_first()
item['bkHref'] = bk.xpath('./@href').extract_first()
yield scrapy.Request(item["bkHref"], callback=self.parse_frist, meta={"item": deepcopy(item)})
def parse_frist(self, response):#解析
item = response.meta["item"]
book_lists = response.xpath('//div[@id="filter-results"]/ul/li')
print(len(book_lists))
for book in book_lists:
item['bkName'] = book.xpath('.//div[@class="res-info"]/p[2]/a/text()').extract_first()
price1 = str(book.xpath('.//div[@class = "res-info"]/p[1]/em/text()').extract_first())
price2 = str(book.xpath('//*[@id="filter-results"]/ul/li[1]/div/div/div/div[2]/p[1]/em/i[1]/text()').extract_first())
item["price"] = price1 + price2
item['commentNumber'] = book.xpath('.//div[@class="res-info"]/p[3]/a[1]/text()').extract_first()
item['bkShop'] = book.xpath('.//div[@class="res-info"]/p[4]/a[1]/text()').extract_first()
item['bkUrl'] = "https:" + book.xpath('.//div[@class="res-info"]/p[2]/a[1]/@href').extract_first()
yield scrapy.Request(item["bkUrl"], callback=self.parse_detail, meta={"item": deepcopy(item)})
def parse_detail(self, response):
item = response.meta["item"]
item["author"] = response.xpath('//li[@class="pb-item"][1]/text()').extract_first() if response.xpath('//li[@class="pb-item"][1]/text()').extract_first() is not None else "未知"
item["press"] = response.xpath('//li[@class="pb-item"][2]/text()').extract_first() if response.xpath('//li[@class="pb-item"][2]/text()').extract_first() is not None else "未知"
item["publishTime"] = response.xpath('//li[@class="pb-item"][3]/span[2]/text()').extract_first() if response.xpath('//li[@class="pb-item"][3]/span[2]/text()').extract_first() is not None else "未知"
yield item
print(item)
错误写法:
def parse(self, response):
book_type = response.xpath('//div[@class="menu-item"]/dl/dt')
book_href = resonse.xpath('//div[@class="menu-item"]/dl/dd')
item = {}
for book in book_type:
item['bkType'] = book.xpath('./h3/a/text()').extract_first()
for bk in book_href:
item['bkTypes'] = bk.xpath('./a/text()').extract_first()
item['bkHref'] = bk.xpath('./a/@href').extract_first()
yield scrapy.Request(item["bkHref"], callback=self.parse_frist, meta={"item": deepcopy(item)})
这样也能爬取数据但是循环的是整个的小标题,不管这个大标题下有没有都循环输出,导致大小分类出现错误,也就是数据不对应。开始我就天真的下来了,后面查看数据的时候发现少儿下居然有4级书就觉得这个数据不对应。所以就用了我上面的写法通过大类来循环小类。下面是我做大小分类测试时候,控制台输出很明显能看出来区别。期刊杂志和进口原版书的页面布局与其他的不一样这里就暂不做爬取。
正确截图:
错误截图:
Middleware这里我只是稍微改动了下,爬取的是单页面的数据,分类下来也很多了。
class SeleniumDownloaderMiddleware:
"""
构造方法
"""
def __init__(self):
self.browser = webdriver.Chrome(r'C:\Program Files (x86)\Google\Chrome\Application\chromedriver.exe') # 每个spider初始化一次driver
"""
动态网页处理中间件
"""
def process_request(self, request, spider):#普通selenium的使用
if spider.name == 'suningDemoSpider' and not (request.url.startswith("https://product")):
# 根据特定Spider 决定是否利用selenium模拟浏览器爬取
self.browser.get(request.url) # selenium请求了网页 获得的Response 因此process_request不需要再返回Request给Downloader
time.sleep(10) # 延时10s 待网页完全加载后打印html文本
print("访问:", request.url)
print("current_url", self.browser.current_url)
# urlchangecnt = 0
# while (urlchangecnt < 3) :
self.browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')
time.sleep(10)
body = self.browser.page_source
# self.browser.find_element_by_id('nextPage').send_keys(Keys.ENTER)
# urlchangecnt = urlchangecnt + 1
print("二级访问:", request.url)
return HtmlResponse(self.browser.current_url, body=body,
encoding="utf-8", request=request) # 这里必须返回Response的类或派生类
#只有这样Scrapy才不会返回Request给Downloader
----------------------------------------8.20更新-------------------------------------------------
早就更改了项目,一直没时间来更新。
上面的项目存在几个问题,爬取的速度慢,数据获取不完全和报错,这次针对这三个问题做了更改。其实之前这个项目也在报错但我没注意这也是其中导致数据量获取不完全的原因。
主要是在settings上做文章
# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT = 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16'
# Obey robots.txt rules
ROBOTSTXT_OBEY = False
LOG_LEVEL = 'WARNING'
LOG_FILE = 'log.txt'
CONCURRENT_REQUESTS = 1000
COOKIES_ENABLED = False
RETRY_ENABLED = False
CONCURRENT_REQUESTS_PER_DOMAIN = 10000
CONCURRENT_REQUESTS_PER_IP = 0
# DOWNLOAD_TIMEOUT = 20
首先说报错,由于我是在上次的项目上进行迭代的。则保留了timeout =3,就有些链接会超时,这样就得不全数据,我就调高了等待时间,也不行,后来查阅了一下更改了请求头将原来的http更改成https这样才好。剩下的仅仅是写微调这里就不做太多展示了。还写了一个爬取更多页面,分类更加的细致。这两者大同小异只是这个数据量更大,以至于跑一遍不想再跑第二遍。爬取页面部分截图见下图。等过段时间把项目都放在GitHub上吧。