【Scrapy学习心得】爬虫实战四(动态加载的页面数据获取)

【Scrapy学习心得】爬虫实战四(动态加载的页面数据获取)

声明:仅供技术交流,请勿用于非法用途,如有其它非法用途造成损失,和本博客无关

爬取的网站:今日头条各个板块的新闻信息 点击跳转

本次爬虫使用的是:scrapy+selenium

一、配置环境

  • python3.7
  • pycharm
  • Scrapy1.7.3
  • win10
  • pymysql

二、准备工作

  • cmd命令行中进入需要创建项目的目录运行scrapy startproject haha
  • 创建成功后继续执行cd haha
  • 然后执行scrapy genspider tt toutiao.com
  • 最后在spider文件夹下可以看到刚创建的tt.py爬虫文件

三、分析网页


可以在页面的左边找到对应板块的url,并且会发现,页面是通过AJAX加载页面的,就是鼠标滑轮往下滚动,页面内容才会加载新的出来,所以此时如果用requests来爬的话,返回的信息肯定是不多的,那么简单粗暴的方法就是用selenium来爬了,那么scrapy怎么使用selenium呢,很简单,只需添加一个中间件就行了

所以总共我要爬取的内容有:

  • 分类的名称以及其具体的url地址
  • 新闻的标题以及对应的url地址

查找元素的那些操作我就不放上来了,因为没什么难度的,会来学scrapy框架的同学肯定是跟我一样那些什么requests啊,urllib啊,selenium啊等等都是用腻了才来的,是吧

四、爬取数据

下面首先放上最重要的middlewares.py的代码:

from scrapy import signals
import time
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from scrapy.http.response.html import HtmlResponse

class seleniumDownloaderMiddleware(object):
    def __init__(self):
        options = webdriver.ChromeOptions()
        # options.add_argument('disable-infobars') #去除警告
        options.add_argument('headless') #无头模式
        self.driver = webdriver.Chrome(options=options)
        # self.driver.maximize_window()
        self.num = 10  #需要滑滚页面的次数,想拿多一点数据就设置的大一点

    def process_request(self,request,spider):
    	#第一次请求只需拿到各个板块的url
        if request.url == 'https://www.toutiao.com/':
            self.driver.get(request.url)
            time.sleep(1)
            html = self.driver.page_source
            response = HtmlResponse(
                url=self.driver.current_url,
                body=html,
                request=request,
                encoding="utf-8"
            )
            return response
        else:
            self.driver.get(request.url)
            for i in range(self.num):
                ActionChains(self.driver).send_keys([Keys.END, Keys.PAGE_UP]).perform()
                time.sleep(1)
            html = self.driver.page_source
            response = HtmlResponse(
                url=self.driver.current_url,
                body=html,
                request=request,
                encoding="utf-8"
            )
            return response

下面是爬虫文件tt.py的代码如下:

# -*- coding: utf-8 -*-
import scrapy

class TtSpider(scrapy.Spider):
    name = 'tt'
    allowed_domains = ['toutiao.com']
    start_urls = ['https://www.toutiao.com/']

    def parse(self, response):
        li_list = response.xpath('//div[@class="channel"]/ul/li')
        for li in li_list:
            item = {}
            url = li.xpath('./a/@href').get()
            url = response.urljoin(url)
            name = li.xpath('.//span/text()').get()
            if name not in ['推荐', '西瓜视频', '直播', '图片', '懂车帝', '更多']:
                item['url']=url
                item['name']=name
                yield scrapy.Request(item['url'],callback=self.parse_news,meta={'item':item})

        li_list1 = response.xpath('//ul[@class="bui-box"]/li')
        for li in li_list1:
            item={}
            url = li.xpath('./a/@href').get()
            url = response.urljoin(url)
            name = li.xpath('.//span/text()').get()
            item['url'] = url
            item['name'] = name
            yield scrapy.Request(item['url'], callback=self.parse_news, meta={'item': item})

    def parse_news(self,response):
        item=response.meta['item']
        li_list = response.xpath('//div[@class="wcommonFeed"]/ul/li')
        for li in li_list:
            if li.xpath('.//a[@class="link title"]/text()').get() is not None:
                title = li.xpath('.//a[@class="link title"]/text()').get()
                title_url = li.xpath('.//a[@class="link title"]/@href').get()
                title_url = response.urljoin(title_url)
                item['title']=title
                item['title_url']=title_url
                yield item

五、保存数据

直接保存到pymysql数据库当中,修改pipeline.py文件,代码如下:

import pymysql

class HahaPipeline(object):
    def __init__(self):
        dbparams = {
            'host': 'localhost',
            'user': 'youruser',
            'password': 'yourpassword',
            'database': 'yourdatabase',
            'charset': 'utf8'
        }
        self.conn=pymysql.connect(**dbparams)
        self.cursor=self.conn.cursor()
        self.sql='insert into tt(name,title,title_url) values(%s,%s,%s)'

    def process_item(self, item, spider):
        self.cursor.execute(self.sql, [item['name'], item['title'], item['title_url']])
        self.conn.commit()

    def close_spider(self,spider):
        self.cursor.close()
        self.conn.close()

现在我们的爬虫大致已经是写完了,不过我还要修改一下setting.py文件的一些设置,需要增加的语句有:

LOG_LEVEL='WARNING' #设置日志输出级别
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'  #设置请求头
ROBOTSTXT_OBEY = False  #把这个设置成False,就不会去请求网页的robots.txt,因为不改为False的话,scrapy就会去访问该网站的robots.txt协议,如果网站没有这个协议,那么它就不会去访问该网站,就会跳过,进而爬不到数据
ITEM_PIPELINES = {
   'haha.pipelines.HahaPipeline': 300,
}
DOWNLOADER_MIDDLEWARES = {
   'haha.middlewares.seleniumDownloaderMiddleware': 543,
}

最后在cmd中先进入到这个项目的根目录下,即有scrapy.cfg文件的目录下,然后输入并运行scrapy crawl tt,最后静静等待就行了

六、数据展示

经过几分钟后,数据库里面会出现以下数据:

写在最后

这次主要是想在scrapy中实现selenium来处理请求返回动态加载的网页数据,进而在scrapy中完美爬取动态页面,不过,你会发现它是等所有的网页请求完毕之后才会有输出,这个问题待解决,不过只要代码写正确了,也不影响哈哈,除非网络断了那数据就不能及时保存,爬虫就白跑了,关键一点这里不能用异步保存数据,会保存有重复的数据目前由于时间的原因,我没有再去探究这个问题,希望有大佬给我指点指点

  • 6
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为您提供Scrapy爬取58同城二手房的代码。但是,请您注意,Scrapy爬虫需要遵守网站的robots.txt协议,不得对网站进行恶意爬取,否则会对您的程序和IP地址产生不好的影响。此外,对于爬取到的信息,请您务必遵守相关法律法规,不得进行商业化利用或其他非法用途。 以下是Scrapy爬取58同城二手房的示例代码: ```python import scrapy from scrapy import Request from scrapy.exceptions import CloseSpider from scrapy.loader import ItemLoader from scrapy.loader.processors import MapCompose, TakeFirst from w3lib.html import remove_tags import re import pymysql class Cq58ErshoufangSpider(scrapy.Spider): name = 'cq_58_ershoufang' allowed_domains = ['cq.58.com'] start_urls = ['https://cq.58.com/ershoufang/'] custom_settings = { 'ROBOTSTXT_OBEY': True, 'DOWNLOAD_DELAY': 1 } def parse(self, response): item_links = response.xpath('//ul[@class="house-list-wrap"]/li[@class="house-cell"]/div[@class="list-info"]/h2/a/@href') for item_link in item_links: yield Request(item_link.get(), callback=self.parse_item) next_page_url = response.css('a.next::attr(href)').get() if next_page_url is not None: yield Request(next_page_url, callback=self.parse) def parse_item(self, response): item_loader = ItemLoader(item=Cq58ErshoufangItem(), response=response) item_loader.default_output_processor = TakeFirst() item_loader.add_xpath('title', '//div[@class="house-title"]/h1/text()') item_loader.add_xpath('sell_point', '//div[@class="house-desc-item"]/div[1]/text()', MapCompose(remove_tags)) item_loader.add_xpath('community_name', '//div[@class="house-desc-item"]/div[2]/a/text()') item_loader.add_xpath('community_address', '//div[@class="house-desc-item"]/div[2]/span/text()') item_loader.add_xpath('room_type', '//ul[@class="house-desc-list"]/li[1]/text()') item_loader.add_xpath('floor', '//ul[@class="house-desc-list"]/li[2]/text()') item_loader.add_xpath('year_built', '//ul[@class="house-desc-list"]/li[3]/text()', MapCompose(self.extract_year_built)) item_loader.add_xpath('unit_price', '//div[@class="price"]/b/text()', MapCompose(remove_tags)) item_loader.add_xpath('total_price', '//div[@class="price"]/span[@class="price-det"]/text()', MapCompose(remove_tags)) yield item_loader.load_item() def extract_year_built(self, value): match = re.search(r'\d{4}', value) if match: return match.group(0) else: return None class Cq58ErshoufangItem(scrapy.Item): title = scrapy.Field() sell_point = scrapy.Field() community_name = scrapy.Field() community_address = scrapy.Field() room_type = scrapy.Field() floor = scrapy.Field() year_built = scrapy.Field() unit_price = scrapy.Field() total_price = scrapy.Field() class Cq58ErshoufangPipeline: def __init__(self, mysql_host, mysql_port, mysql_db, mysql_user, mysql_password): self.mysql_host = mysql_host self.mysql_port = mysql_port self.mysql_db = mysql_db self.mysql_user = mysql_user self.mysql_password = mysql_password @classmethod def from_crawler(cls, crawler): return cls( mysql_host=crawler.settings.get('MYSQL_HOST'), mysql_port=crawler.settings.get('MYSQL_PORT'), mysql_db=crawler.settings.get('MYSQL_DB'), mysql_user=crawler.settings.get('MYSQL_USER'), mysql_password=crawler.settings.get('MYSQL_PASSWORD') ) def open_spider(self, spider): self.conn = pymysql.connect( host=self.mysql_host, port=self.mysql_port, db=self.mysql_db, user=self.mysql_user, password=self.mysql_password, charset='utf8mb4' ) self.cursor = self.conn.cursor() def close_spider(self, spider): self.conn.close() def process_item(self, item, spider): sql = ''' INSERT INTO cq_58_ershoufang ( title, sell_point, community_name, community_address, room_type, floor, year_built, unit_price, total_price ) VALUES ( %s, %s, %s, %s, %s, %s, %s, %s, %s ) ''' data = ( item['title'], item['sell_point'], item['community_name'], item['community_address'], item['room_type'], item['floor'], item['year_built'], item['unit_price'], item['total_price'] ) self.cursor.execute(sql, data) self.conn.commit() return item ``` 在该示例代码中,我们使用了Scrapy框架进行网页爬取,并使用了XPath和CSS选择器对网页元素进行定位和提取。爬取到的信息存储在一个自定义的Item对象中,并使用ItemLoader对数据进行处理。最后,我们将爬取到的数据存储在MySQL数据库中,需要在settings.py文件中配置MySQL数据库的相关参数。 需要注意的是,该示例代码只是一个参考,具体的爬取需求和数据存储方式可能需要根据您的实际情况进行调整。另外,为了防止网站的反爬虫机制,我们在settings.py中设置了DOWNLOAD_DELAY参数,即每次请求之间的时间间隔,您可以根据需要进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值