数据采集与融合技术实践3

数据采集与融合技术实践3

任务一

代码和图片

任务要求指定一个网站,爬取这个网站中的所有的所有图片,例如:中国气象网(http://www.weather.com.cn)。实现单线程和多线程的方式爬取。
–务必控制总页数(学号尾数2位)、总下载的图片数量(尾数后3位)等限制爬取的措施,我的学号是132301146,所以我的总页数应该控制在46页之内(一般达不到),下载的图片量最多为146张。

单线程方法:

1.进入一个页面,并抓取所有图片并马上下载。
这是中国气象网站的部分数据,其中,我们可以发现,其主要的图片链接保存在<img>块中:
image
因此,我使用soup.find_all("img")方法,获得所有<img>块,并下载其中内容。
相关代码展示:

        for img in soup.find_all("img"):
            if img_cnt>=max_images:
                break
            src=img.get("src")
            if not src:
                continue
            img_url=urljoin(url,src)
            if not img_url.startswith("http"):
                continue
            #这里的downloaded_imgs为已经下载过的链接的集合,如果这个链接已经下过,就跳过
            if img_url in downloaded_imgs:
                continue
            downloaded_imgs.add(img_url)
            img_cnt+=1
            print("第",img_cnt, "张图片:",img_url)
            try:
                ir=requests.get(img_url,headers=headers,timeout=10)
            except:
                continue
            #下载图片,并将其格式全部转为jpg
            if ir.status_code==200:
                file_path=os.path.join(image_dir, f"single_{img_cnt:03d}.jpg")
                with open(file_path, "wb") as f:
                    f.write(ir.content)

2.收集页面中的链接,并加入待访问队列。
一张页面一般不会有146张图片,所以我们要多找几个页面爬取,我以主页面为出发点,爬取主页面中的链接并访问,如此递归
我们可以发现,url数据存在<a>块的herf中:
image
因此,我使用soup.find_all("a")a.get("href")来提取url
相关代码展示:

        #加入新的页面链接
        for a in soup.find_all("a"):
            href=a.get("href")
            #没有就跳过
            if not href:
                continue
            new_url=urljoin(url, href)
            if "weather.com.cn" in new_url and new_url.startswith("http"):
                #保证链接不重,防止环状结构
                if new_url not in visited and new_url not in to_visit:
                    to_visit.append(new_url)

部分结果图片展示:
images文件夹:
image
终端:
image
可见,146张图片的爬取耗时是23.81秒,速度较慢,这是因为单线程方法是一张一张地爬取图片,速率较低。

多线程方法:

1.先遍历页面,仅负责收集图片URL,但是并不下载图片
这里的代码和单线程差不多,唯一不同就是先不下载图片,只是收集图片的url存入集合中并去重。故不重复展示代码
2.使用threading.Thread启动多个线程并发下载
收集完146张图片的url后,使用threading.Thread并发下载图片:
相关代码展示:

    #多线程下载
    threads=[]
    idx=0
    for img_url in image_urls:
        idx+=1
        file_path=os.path.join(image_dir, f"multi_{idx:03d}.jpg")
        print("准备下载:", img_url)
        t=threading.Thread(target=urllib.request.urlretrieve, args=(img_url, file_path))
        t.start()
        threads.append(t)
    for t in threads:
        t.join()

结果截图:
images文件夹:
image
终端:
image
可见这次只用了2.8秒就下载完了146张图片,速度明显加快,这是因为多线程是并行下载图片,效率较高。

作业心得

通过这次作业,我完整实现了从爬取页面到解析HTML到提取图片到保存文件的完整数据采集流程,感觉收获非常大。在单线程模式里,我能比较清楚地看到爬虫的执行顺序,但速度确实慢;在多线程模式中,我真正体会到了并发带来的效率提升。尤其是在图片较多的情况下,多线程的下载速度可以说是成倍增长。同时我也意识到了爬虫开发中需要注意的几个关键点,比如url和图片链接需要去重,不然很可能一直爬同样的图片。

任务二

代码和图片

任务要求熟练掌握 scrapy 中 Item、Pipeline 数据的序列化输出方法;使用scrapy框架+Xpath+MySQL数据库存储技术路线爬取股票相关信息。
首先,在终端创建scrapy项目stock_spider
6c49e652420667ee71e59940bd1a0bdf
接着,修改items.py里面的代码
改成如下形式:

import scrapy

class StockSpiderItem(scrapy.Item):
    # 股票代码
    bStockNo=scrapy.Field()#股票名称
    bStockName=scrapy.Field()#最新报价
    bNewPrice=scrapy.Field()#涨跌幅
    bChangeRate=scrapy.Field()#涨跌额
    bChangeAmt=scrapy.Field()#成交量
    bVolume=scrapy.Field()#成交额
    bTurnover=scrapy.Field()#振幅
    bAmplitude=scrapy.Field()#最高
    bHigh=scrapy.Field()#最低
    bLow=scrapy.Field()#今开
    bOpenToday=scrapy.Field()#昨收
    bCloseYesterday=scrapy.Field()

接着修改pipeline.py的代码,让其在爬虫开始时链接数据库

        self.conn=pymysql.connect(
            host=self.host,
            port=self.port,
            user=self.user,
            password=self.password,
            db=self.dbname,
            charset='utf8mb4'
        )
        self.cursor = self.conn.cursor()
        print("已连接 MySQL 数据库")

并设置插入语句

        sql = """
        INSERT INTO {table}
        (bStockNo, bStockName, bNewPrice, bChangeRate, bChangeAmt,
         bVolume, bAmplitude, bHigh, bLow, bOpenToday, bCloseYesterday)
        VALUES (%s, %s, %ss, %s, %s, %s, %s, %s, %s, %s, %s)
        """.format(table=self.table)

然后,修改settings.py中的配置,设置头部等信息。
最后编写eastmoney_spider.py脚本
对于网站的解析和上一次实践博客中的大致相同,网站结构为
image

image
关于解析提取数据方面,本次实验用 Xpath 取出整个响应体内容,再用再用 json.loads 把这个字符串解析成 Python 字典,并且在提取数据后,程序会将每只股票的数据填充到item里,最后用yield item将数据交给Scrapy的Pipeline
相关代码:
Xpath部分:

        sel=Selector(text=response.text)
        #用xpath取出文本
        json_text=sel.xpath("string(.)").get()

        try:
            data=json.loads(json_text)
        except Exception as e:
            print("解析出错:", e)

item部分:

for d in diff_list:
    item=StockSpiderItem()

    item["bStockNo"]=d.get("f12")
    item["bStockName"]=d.get("f14")
    item["bNewPrice"]=d.get("f2")
    item["bChangeRate"]=d.get("f3")
    item["bChangeAmt"]=d.get("f4")
    item["bVolume"]=d.get("f5")
    item["bTurnover"]=d.get("f6")
    item["bAmplitude"]=d.get("f7")
    item["bHigh"]=d.get("f15")
    item["bLow"]=d.get("f16")
    item["bOpenToday"]=d.get("f17")
    item["bCloseYesterday"]=d.get("f18")

最后,提前在mysql中建好表,并在stock_spider路径下运行scrapy crawl eastmoney_spider开始爬取数据
结果截图:
终端:
image
sql查询结果:
image

作业心得

这次实验主要使用Scrapy+Xpath+MySQL来完成东方财富网股票数据的爬取。整个过程让我熟悉了Scrapy的基本结构,包括Spider用来发送请求、Xpath用来提取数据、Pipeline负责把数据保存到 MySQL中。相比之前用requests的方式,Scrapy的流程更清晰,也更适合做规模化的爬虫。在编写实验代码时,我遇到了一些问题,比如数据库连接失败、数据表不存在、Xpath取值为空等,但通过不断调试,我逐渐掌握了Scrapy中 Item、Pipeline的使用方法,也学会了如何在MySQL中查看和管理爬取的数据,完成了数据库与爬虫的融合。

任务三

代码和图片

任务要求熟练掌握 scrapy 中 Item、Pipeline 数据的序列化输出方法;使用scrapy框架+Xpath+MySQL数据库存储技术路线爬取外汇网站数据。
这里,我选择的目标网站是中国银行网(https://www.boc.cn/sourcedb/whpj/)
首先,观察网站结构:
image
由图片可知,每一组外汇数据储存在一个个<tr>块里,而每个<tr>块里又会有一组<td>储存具体数值
因此我先用 Xpath 找到整张外汇牌价表的每一行,然后对于每一行,用 Xpath 取出所有单元格文本,其中,货币名称,现汇买入价,现钞买入价,现汇卖出价,现钞卖出价,发布时间分别对应第0,1,2,3,4,6个<td>块,在爬取完数据之后,把数据组装成item,通过pipline存储到数据库里。
相关代码展示:

        rows=response.xpath("//table//tr")

        #第一行通常是表头,跳过
        for tr in rows[1:]:
            tds=tr.xpath("./td/text()").getall()
            #有些tr可能是空行,这种就不要了
            if len(tds) < 7:
                continue

            currency=tds[0].strip()
            tbp=tds[1].strip()
            cbp=tds[2].strip()
            tsp=tds[3].strip()
            csp=tds[4].strip()
            time_str=tds[6].strip()

            if not currency:
                continue
            item=BocFxItem()
            item["Currency"]=currency
            item["TBP"]=self.to_float(tbp)
            item["CBP"]=self.to_float(cbp)
            item["TSP"]=self.to_float(tsp)
            item["CSP"]=self.to_float(csp)
            item["Time"]=time_str
            print("抓到:", item["Currency"], item["TBP"], item["CBP"], item["Time"])
            yield item

步骤和任务2差不多。
结果展示:
终端:
image

数据库:
image

作业心得

通过本次实验,我了解了 Scrapy 框架的完整数据采集流程,包括 Spider 的编写、XPath 数据提取、Item 数据结构设计,以及 Pipeline 结合 MySQL 实现持久化存储。更加熟悉了爬虫技术与数据库之间的协调

gitee链接

https://gitee.com/hhz07734/parse/tree/master/practice_3

posted @ 2025-11-17 12:38  正三???  阅读(12)  评论(0)    收藏  举报