数据采集与融合技术实践作业三

news/2024/12/28 23:48:16/文章来源:https://www.cnblogs.com/mysterious-x/p/18522908

数据采集与融合技术实践作业三

gitee链接:https://gitee.com/wei-yuxuan6/myproject/tree/master/作业3

作业①

Scrapy爬取图片实验

  • 要求:指定一个网站,爬取这个网站中的所有的所有图片,例如:中国气象网(http://www.weather.com.cn)。使用scrapy框架分别实现单线程和多线程的方式爬取。

  • 务必控制总页数(学号尾数2位)、总下载的图片数量(尾数后3位)等限制爬取的措施。

  • 输出信息: 将下载的Url信息在控制台输出,并将下载的图片存储在images子文件中,并给出截图。

  • 过程
    通过在网页HTML中查找图片元素发现图片的链接都在<img>元素的src属性中(这个网站的格式比较统一)

    就可以直接使用 Selector 类解析 HTML 并使用XPATH定位<img>和获取属性src
    pics = selector.xpath("//img/@src")
    使用extract()提取图片URL
    picurl = pic.extract()
    使用Scrapy需要使用命令创建爬虫,scrapy startproject命令创建爬虫项目

    scrapy genspider <爬虫名称> <域名>创建爬虫程序

    可以在本地文件夹中查看是否创建成功

    至于实现多线程和单线程,Scrapy本身并不直接支持多线程,因为它是基于异步IO的。但是可以通过改变settings中CONCURRENT_REQUESTS的值实现类似多线程的效果,设置并发请求数,为1近似单线程,也可以设置成其他值近似多线程(默认16)

      CONCURRENT_REQUESTS = 1
    
  • 部分代码
    1)先写items.py文件定义数据格式和字段个数

    import scrapyclass Job1Item(scrapy.Item):num=scrapy.Field() #图片编号picurl = scrapy.Field() #图片URL
    

    2)再写爬虫程序picspider.py文件
    必须导入定义的Item类

    from job1.items import Job1Item
    

    构造初始URL并解析响应,Job1Item 是用于存储爬取结果的对象,通过 yield 将每个包含图片 URL 和编号的 item 传递给 ItemPipelines处理

    # 定义一个爬虫类,继承自scrapy.Spider
    class PicspiderSpider(scrapy.Spider):name = "picspider"  # 爬虫的名称,用于命令行启动start_urls = ["http://www.weather.com.cn"]  # 初始请求的URL列表num = 0  # 用于计数图片的编号# 解析响应的方法def parse(self, response):try:# 使用UnicodeDammit来处理响应体的编码,自动检测并转换为合适的编码dammit = UnicodeDammit(response.body, ["utf-8", "gbk"])data = dammit.unicode_markup  # 获取转换后的HTML内容# 使用Selector解析转换后的HTML数据selector = Selector(text=data)# 使用XPath选择所有图片的src属性pics = selector.xpath("//img/@src")# 遍历所有图片的srcfor pic in pics:self.num += 1  # 每次循环编号加1picurl = pic.extract()  # 提取图片URLitem = Job1Item()  # 创建一个Item实例item['num'] = self.num  # 设置Item的num属性item['picurl'] = picurl  # 设置Item的picurl属性# 使用yield生成Item对象yield item# 异常处理,打印错误信息except Exception as e:print(e)

    3)写pipelines.py文件(如何处理scrapy爬取到的数据)
    这里使用了urllib.request.urlretireve方法从获取到的图片URL下载图片并保存到本地

    class Job1Pipeline():def process_item(self, item, spider):print("正在下载 "+item['picurl'])urllib.request.urlretrieve(item['picurl'], f'./images/第{item["num"]}张图片.jpg')
    

    4)配置settings.py文件
    添加浏览器代理

    USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 " \"Safari/537.36"
    

    不遵守robots.txt

    ROBOTSTXT_OBEY = False
    
    CONCURRENT_REQUESTS = 1
    

    设置download_delay,防止访问过快被拒绝访问

    DOWNLOAD_DELAY = 3
    

    打开item_pipelines
    4)run.py文件
    使得scrapy可以直接在Pycharm中输出

    from scrapy import cmdline
    cmdline.execute("scrapy crawl picspider -s LOG_ENABLED=False".split())
    
  • 结果
    gitee链接:https://gitee.com/wei-yuxuan6/myproject/tree/master/作业3/job1


    还尝试在pipelines.py中继承ImagesPipeline,并重写get_media_requests、file_path 和 item_completed方法,以实现自定义的图片下载逻辑和文件存储路径

    class Job1Pipeline(ImagesPipeline):def get_media_requests(self, item, info):yield scrapy.Request(item['picurl'])def file_path(self, request, response=None, info=None, *, item=None):print("正在下载 "+request.url)num = item['num']return f'第{num}张图片.jpg'def item_completed(self, results, item, info):return item
    

    在settings.py中指定图片保存文件夹

    # 指定文件保存位置
    IMAGES_STORE = "./images2"
    

    可以看到它不会再次访问已经访问过的URL,即重复的图片只下载一次

    但是比使用urllib.request爬取到的图片还少了几张,主要是云图、雷达、降水量预报图(不知道是什么原因,尝试了很多次都是少这几张)

  • 爬取京东的商品图片实现翻页,从第50页开始爬取150项
    gitee链接:https://gitee.com/wei-yuxuan6/myproject/tree/master/作业3/jdpic
    京东图片URL不仅在元素的src属性中,还有部分在data-lazy-img中,所以需要分别提取这两个属性的值,需要在访问完一页后构造新的URL并发出请求

    class SpiderJdSpider(scrapy.Spider):name = "picspider"  # 爬虫名称source_url = "https://search.jd.com/"  # JD商品搜索的基础URLq = "电脑"  # 要搜索的商品关键词num = 150  # 用户输入的商品数量# 计算需要抓取的页面数量,每页显示30个商品if num % 30 == 0 and num != 0:page = num // 30  # 如果num是30的倍数else:page = num // 30 + 1  # 否则,加一页begin = 50  # 初始化页面计数器,表示当前抓取的页面(从第50页开始)i = 0  # 初始化抓取页数计数器count = 0  # 初始化商品计数器,记录成功抓取的商品数量def start_requests(self):# 构建请求URL,包含搜索关键词和其他参数url = self.source_url + "Search?keyword=" + urllib.parse.quote(self.q) + \"&wq=" + urllib.parse.quote(self.q) + \" f731e1b710764540abce5caf912eeb7a & isList = 0 & page = " + \str(self.begin) + "&s = 56 & click = 0 & log_id = 1730513540494.9221"yield scrapy.Request(url=url, callback=self.parse)  # 发送请求,回调解析函数def parse(self, response):print(response.url)  # 打印当前请求的URLdammit = UnicodeDammit(response.body, ["utf-8", "gbk"])  # 处理响应体的编码data = dammit.unicode_markup  # 获取处理后的HTML内容selector = Selector(text=data)  # 使用Selector解析HTMLitems = selector.xpath("//ul[@class='gl-warp clearfix']/li")  # 选择商品列表项for item in items:url = item.xpath(".//div[@class='p-img']//img/@src").extract_first()if not url:url = item.xpath(".//div[@class='p-img']//img/@data-lazy-img").extract_first()picurl="https:"+urlpicurl = re.sub(r'\.avif$', '', picurl)self.count += 1  # 商品计数器加1item = JdpicItem()  # 创建JdItem实例item['num'] = self.countitem['picurl'] = picurlyield item  # 发送抓取到的商品信息if self.count == self.num:  # 如果已抓取到所需数量break  # 退出循环self.i += 1  # 页数计数器加1if self.i < self.page:  # 如果还有页面需要抓取# 构建下一页的请求URLurl = self.source_url + "Search?keyword=" + urllib.parse.quote(self.q) + \"&wq=" + urllib.parse.quote(self.q) + \" f731e1b710764540abce5caf912eeb7a & isList = 0 & page = " + \str(self.begin+self.i) + "&s = 56 & click = 0 & log_id = 1730513540494.9221"yield scrapy.Request(url=url, callback=self.parse)  # 发送请求,继续抓取下一页
    

    在settings中设置

    COOKIES_ENABLED = False
    

    爬取京东的pipelines.py,还是使用urllib.request
    因为尝试使用继承ImagePipelines只能下载30张图片,可能被限制访问了
    150张图片使用urllib.request.urlretrieve下载很慢,要十几分钟,所以可以在这里使用多线程下载

    class JdpicPipeline:def __init__(self):# 创建线程池,线程数量可根据需要调整self.executor = ThreadPoolExecutor(max_workers=5)  # 5 个线程并发下载def process_item(self, item, spider):# 提交下载任务到线程池self.executor.submit(self.download_image, item)return itemdef download_image(self, item):try:print(f"正在下载 {item['picurl']}")urllib.request.urlretrieve(item['picurl'], f'./images3/第{item["num"]}张图片.jpg')print(f"下载完成 {item['picurl']}")except Exception as e:print(f"下载失败 {item['picurl']} - 错误: {e}")def close_spider(self, spider):# 关闭线程池并等待所有线程完成self.executor.shutdown(wait=True)print("所有下载任务已完成")
    

    结果:

    一共150张图片

心得体会

初步实践了使用Scrapy下载图片,在Scrapy框架中传递图片URL数据并在ItemPipelines实现图片下载
巩固了翻页访问网页,以及在spider中构造新的URL请求并获得和解析响应,还有Scrapy的工作流程
两个实践都想采用继承ImagePipelines的方式下载图片但是都没有得到很好的结果,可能是对该方法的掌握和理解还不够深入,需要进一步加强学习。
而且在爬取天气网的图片时还有一些图片url在html中没有被访问和下载,可能是因为这些图片是动态的需要动态加载。

作业②

动态爬取股票相关信息实验

  • 要求:熟练掌握 scrapy 中 Item、Pipeline 数据的序列化输出方法;使用scrapy框架+Xpath+MySQL数据库存储技术路线爬取股票相关信息。

  • 候选网站:东方财富网:https://www.eastmoney.com/

  • 输出信息:MySQL数据库存储和输出格式如下:

  • 过程
    访问东方财富网,找到需要爬取的股票html,看到股票信息都在表格<tbody>中,每个股票在一个<tr>元素中,字段分别在不同的<td>中,需要找到自己需要的字段(如序号在第一个td 中……)

    而且因为这个网站的数据是实时加载的,但是Scrapy是静态的,不能获取JavaScript执行过的html,所以如果不与Selenium配合使用就不能获得数据,而且Selenium相关的处理程序需要写在中间件中,因为downloader会得到spider请求响应的html,需要在中间件中使用Selenium重新处理html使得是JavaScript执行过的,并封装成新的响应发送给spider解析

    from scrapy.http import HtmlResponse
    from selenium import webdriverclass SeleniumMiddleware(object):def process_request(self,request,spider):url = request.urlbrowser = webdriver.Chrome()browser.get(url)time.sleep(5)html = browser.page_sourcebrowser.close()return HtmlResponse(url=url, body=html, request=request, encoding="utf-8", status=200)
    

    同时需要在settings.py中配置这个中间件

    SELENIUM_ENABLED = True
    DOWNLOADER_MIDDLEWARES = {"job2.middlewares.SeleniumMiddleware": 543,
    }
    

    而且题目要求将爬取到的数据存储到MySQL数据库中,所以新建一个数据库crawl,再在其中建一个表stocks

  • 部分代码
    items.py设置字段

    class Job2Item(scrapy.Item):# define the fields for your item here like:no = scrapy.Field()code= scrapy.Field()name = scrapy.Field()zxj = scrapy.Field()zdf = scrapy.Field()zde = scrapy.Field()cjl = scrapy.Field()zf = scrapy.Field()zg = scrapy.Field()zd = scrapy.Field()jk = scrapy.Field()zs = scrapy.Field()
    

    spi_stock.py
    主要是解析响应部分,因为获得的是JavaScript处理过的html所以可以直接使用Xpath获取所需文本,使用position定位需要字段对应的<td>标签下的文本,再使用extract_first提取出文本值

         def parse(self, response):try:dammit=UnicodeDammit(response.body,["utf-8","gbk"])data=dammit.unicode_markupselector=Selector(text=data)items = selector.xpath("//table[@id='table_wrapper-table']//tbody/tr")for i in items:no = i.xpath(".//td[position()=1]//text()")name = i.xpath(".//td[position()=3]//text()")code = i.xpath(".//td[position()=2]//text()")zxj = i.xpath(".//td[position()=5]//text()")zdf = i.xpath(".//td[position()=6]//text()")zde = i.xpath(".//td[position()=7]//text()")cjl = i.xpath(".//td[position()=8]//text()")zf = i.xpath(".//td[position()=10]//text()")zg = i.xpath(".//td[position()=11]//text()")zd = i.xpath(".//td[position()=12]//text()")jk = i.xpath(".//td[position()=13]//text()")zs = i.xpath(".//td[position()=14]//text()")item=Job2Item()item["no"]=no.extract_first()item["code"]=code.extract_first()item["name"]=name.extract_first()item["zxj"]=zxj.extract_first()item["zdf"]=zdf.extract_first()item["zde"]=zde.extract_first()item["cjl"]=cjl.extract_first()item["zf"]=zf.extract_first()item["zg"]=zg.extract_first()item["zd"]=zd.extract_first()item["jk"]=jk.extract_first()item["zs"]=zs.extract_first()yield itemexcept Exception as e:print(e)
    

    pipelines.py
    也是一个重要的部分,在这里需要打开数据库,将解析后的数据存储到数据库,最后还要关闭数据库
    open_spider在开启爬虫时执行,连接MySQL数据库

        def open_spider(self, spider):print("opened")  # 打印"opened",表示爬虫已启动try:# 连接到 MySQL 数据库self.con = pymysql.connect(host="127.0.0.1", port=3306, user="root", passwd="Tnt191123!", db="crawl",charset="utf8")self.cursor = self.con.cursor(pymysql.cursors.DictCursor)  # 创建一个游标,使用字典游标以便返回字典格式的数据self.cursor.execute("delete from stocks")  # 清空 stocks 表self.opened = True  # 设置状态为已打开# 打印表头print("{:<5}{:<10}{:<10}{:<8}{:<8}{:<10}{:<10}{:<8}{:<10}{:<8}{:<8}{:<8}".format("序号", "代码", "名称", "最新价", "涨跌幅", "涨跌额", "成交量", "振幅", "最高", "最低", "今开", "昨收"))except Exception as e:print(e)  # 打印异常信息self.opened = False  # 如果出错,设置状态为未打开
    

    close_spider在爬虫结束时执行,关闭数据库

       def close_spider(self, spider):if self.opened:  # 检查爬虫是否已打开self.con.commit()  # 提交数据库事务self.con.close()  # 关闭数据库连接self.opened = False  # 设置状态为未打开print("closed")  # 打印"closed",表示爬虫已关闭
    

    process_item在每次接收到spider解析后的数据执行,先打印出数据再将数据插入到stocks表中

      def process_item(self, item, spider):try:# 格式化并打印每个项的内容print("{:<5}{:<13}{:<10}{:<10}{:<10}{:<10}{:<10}{:<15}{:<10}{:<10}{:<10}{:<10}".format(item["no"], item["code"], item["name"], item["zxj"], item["zdf"], item["zde"], item["cjl"], item["zf"],item["zg"], item["zd"], item["jk"], item["zs"]))if self.opened:  # 检查爬虫是否已打开# 执行插入操作,将项数据插入到 stocks 表中self.cursor.execute("insert into stocks(no,code,name,zxj,zdf,zde,cjl,zf,zg,zd,jk,zs) values(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)",(item["no"], item["code"], item["name"], item["zxj"], item["zdf"], item["zde"], item["cjl"],item["zf"],item["zg"], item["zd"], item["jk"], item["zs"]))except Exception as e:print(e)  # 打印异常信息return item  # 返回处理后的项
    
  • 结果
    gitee链接:https://gitee.com/wei-yuxuan6/myproject/tree/master/作业3/job2
    在运行时会出现一个浏览器窗口,加载网页

    打印出的数据

    存入数据库的数据

心得体会

学会了将Selenium与Scrapy结合爬取动态网页,在处理JavaScript动态渲染页面时,需要提前使用Selenium加载页面,使Scrapy能够抓取到完整的HTML内容,这样在Scrapy接收到响应时,页面已经被完全渲染。还学会了在Scrapy框架中将爬取到的数据存入MySQL数据库,进一步掌握了Scrapy与数据库交互的方式。

作业③

爬取银行外汇网站并存储实验

  • 要求:熟练掌握 scrapy 中 Item、Pipeline 数据的序列化输出方法;使用scrapy框架+Xpath+MySQL数据库存储技术路线爬取外汇网站数据。

  • 候选网站:中国银行网:https://www.boc.cn/sourcedb/whpj/

  • 输出信息:

  • 过程
    分析网页发现数据都在表格中,在<table>下的<tbody>中,每一行数据在<tr>中,每个字段在不同的<td>中,其实后面spider获取的响应没有<tbody>元素。

    在MySQL创建一个新的表currency

  • 部分代码
    爬虫程序spi_bank.py,解析html获取数据,这里限定爬取100个数据,在后面执行了翻页操作

    class SpiBankSpider(scrapy.Spider):name = "spi_bank"start_urls = ["https://www.boc.cn/sourcedb/whpj/"]num=100count=0def parse(self, response):dammit = UnicodeDammit(response.body, ["utf-8", "gbk"])data = dammit.unicode_markupselector = Selector(text=data)items = selector.xpath("//table[position()=1]/tr[position()>1]")for i in items:item=Job3Item()name = i.xpath("./td[position()=1]/text()")tbp = i.xpath("./td[position()=2]/text()")cbp = i.xpath("./td[position()=3]/text()")tsp = i.xpath("./td[position()=4]/text()")csp = i.xpath("./td[position()=5]/text()")time = i.xpath("./td[position()=8]/text()")item["name"]=name.extract_first() if name else ""item["tbp"]=tbp.extract_first() if tbp else ""item["cbp"]=cbp.extract_first() if cbp else ""item["tsp"]=tsp.extract_first() if tsp else ""item["csp"]=csp.extract_first() if csp else ""item["time"]=time.extract_first() if time else ""yield itemself.count+=1if self.count==self.num:breakif self.count%27==0 and self.count!=self.num:url="https://www.boc.cn/sourcedb/whpj/index_"+str(self.count//27)+".html"yield scrapy.Request(url=url, callback=self.parse)
    

    pipelines.py的代码与上题基本一致,都是打开、插入和关闭数据库

  • 结果
    gitee链接:https://gitee.com/wei-yuxuan6/myproject/tree/master/作业3/job3
    直接输出结果

    数据库数据,共100项

心得体会

遇到了网页上的html与获取到的响应的html元素不一致的情况,响应到的html没有<tbody>元素,所以刚开始使用<tobody>定位,没有输出但是又不知道错误在哪里……
还学会了使用position()>1,position不仅可以等于,因为第一个tr是表头,而position()>1可以直接获取后面所有的tr
巩固了使用scrapy框架+Xpath+MySQL数据库存储技术路线

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/826184.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

固态移动硬盘不识别数据恢复

当固态移动硬盘不被识别时,数据恢复成为一个关键问题。以下是一些有效的数据恢复方法: 一、检查连接与驱动程序 检查连接:首先确认固态移动硬盘的连接是否正确,包括SATA接口、电源连接(如果适用)以及USB线或其他转接线是否损坏或松动。对于外置硬盘,确保连接线和转接器没…

OpenGL编程指南(原书第9版)

百度网盘下载地址: https://pan.baidu.com/s/1ATLvMOcW1jwBXSygOg3xUA 关注微信公众号 回复 1100 获取提取码:

30 万维网

互联网是传输数据的管道, 万维网是互联网上的上一层,是一个程序,传输最多数据的程序,可以用浏览器访问万维网 超文本,超链接 每个网页有唯一地址,这个地址叫统一资源定位器url 网页有书写标准hyml等等 搜索引擎: 1爬虫,跟着链接到处跑,看到新连接就加进自己的列表 2不…

apisix~相关组件的图形化说明

参考:https://docs.api7.ai/apisix/key-concepts/plugins Apache APISIX是Apache软件基金会下的顶级项目,由API7.ai开发并捐赠。它是一个高性能的云原生API网关,具有动态、实时等特点。 APISIX网关可作为所有业务的流量入口,为用户提供了丰富的功能,包括动态路由、动态上游…

Django Swagger文档库drf-spectacular

在使用DRF的时候,通常的文档有:默认文档RestFrameWork、CoreAPI、Swagger,Swagger是最流行的API文档库,在绝大多数服务端开发中都有用到,之前我们使用了CoreAPI来生成文档,一方面是它不够流行,没办法和其他工具结合,另一方面可能是我不熟悉,所有有些接口并不能按照我们…

Redis常见面试题:ZSet底层数据结构,SDS、压缩列表ZipList、跳表SkipList

String类型底层数据结构,List类型全面解析,ZSet底层数据结构;简单动态字符串SDS、压缩列表ZipList、哈希表、跳表SkipList、整数数组IntSet文章目录 一、Redis数据结构概述1.1 Redis有哪些数据类型 1.2 Redis本质是哈希表 1.3 Redis的哈希冲突与渐进式rehash 1.4 数据结构底…

AQS

目录AQS简介AQS原理AbstractQueuedSynchronizer类state共享变量CLH队列Node属性独占式分析acquire独占式获取锁tryAcquireaddWaiterenqacquireQueuedshouldParkAfterFailedAcquireparkAndCheckInterruptfinally代码块selfInterruptrelease独占式锁释放unparkSuccessoracquireln…

高级语言程序设计第5次作业

高级语言程序设计课程第四次个人作业 这个作业属于哪个课程:https://edu.cnblogs.com/campus/fzu/2024C/ 这个作业要求在哪里:https://edu.cnblogs.com/campus/fzu/2024C/homework/13298 学号:102300303 姓名:梁佳 第八章第1题第2题第3题第4题第5题第6题第7题第8题第九章第…

利用前馈神经网络(FNN)进行气温预测任务

一、前馈神经网络 前馈神经网络(Feedforward Neural Networks, FNN)是人工神经网络中的一种,它的信息流动是单向的,从输入层到隐藏层,再到输入层,没有反向的连接。其中,隐藏层可以有多个,用于处理输入层的数据,且每一个隐藏层通常配合一个非线性的激活函数来进行训练。…

CoSeR桥接图像和语言以实现认知超分辨率

CoSeR桥接图像和语言以实现认知超分辨率 6.10.1 CoSeR桥接图像和语言以实现认知超分辨率概述 现有的超分辨率(SR)模型主要侧重于恢复局部纹理细节,往往忽略了场景中的全局语义信息。这种疏忽可能会导致在恢复过程中遗漏关键的语义细节或引入不准确的纹理。 引入了认知超分辨…

综合、诊断和优化:迈向精细视觉语言理解

综合、诊断和优化:迈向精细视觉语言理解 6.8.1 综合、诊断和优化:迈向精细视觉语言理解概述视觉语言模型(VLM)在各种下游任务中表现出了卓越的性能。然而,理解细粒度的视觉语言概念,如属性和对象间关系,仍然是一个重大的挑战。虽然有几个基准旨在以更精细的粒度评估VLM,…

Nuxt.js 应用中的 nitro:init 事件钩子详解

title: Nuxt.js 应用中的 nitro:init 事件钩子详解 date: 2024/11/3 updated: 2024/11/3 author: cmdragon excerpt: nitro:init 是 Nuxt 3 中的一个生命周期钩子,在 Nitro 初始化完成后被调用。这个钩子允许开发者注册 Nitro 钩子,并直接与 Nitro 进行交互。这种灵活性使…