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

发布时间 2023-11-01 15:37:51作者: 早点儿睡吧

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

gitee仓库链接:https://gitee.com/zhoujingyang0509/crawl_project/tree/master/作业3

作业1

要求:

指定一个网站,爬取这个网站中的所有的所有图片,例如:中国气象网(http://www.weather.com.cn)。

使用scrapy框架分别实现单线程和多线程的方式爬取。

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

输出信息:

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

test.py

解析数据

class WeatherSpider(scrapy.Spider):
    name = 'test'
    # allowed_domains = ['www.xxx.com.cn']
    start_urls = ['http://www.weather.com.cn/']

    page = 0
    pic_num = 0

    #解析图片链接
    def parsePic(self, response):
		#获取图片链接列表
        images = response.xpath("//img")
        for image in images:
            self.pic_num+=1
            img = image.xpath('@src').extract_first()
            #爬取108张图片
            if self.pic_num<=108:
                item = ImgItem()
                item["src"] = img
                item["pic_num"]=self.pic_num
                print("第",self.pic_num,"张:",img)
                yield item

    #解析页面url
    def parse(self, response):
        urls = response.xpath("//a/@href").extract()
        for url in urls:
            self.page += 1
            #爬取8页
            if self.page <= 8:
                time.sleep(2)
                yield scrapy.Request(url=url, callback=self.parsePic)

items.py

定义数据对象

class Test32Item(scrapy.Item):
    # define the fields for your item here like:

    src = scrapy.Field()

pipelines.py

存储数据

from scrapy.pipelines.images import ImagesPipeline

class imgsPileLine(ImagesPipeline):
    # count=1
    #根据图片地址进行数据请求
    def get_media_requests(self, item, info):
        yield scrapy.Request(item['src'])

    #指定图片存储路径
    def file_path(self, request, response=None, info=None, *, item=None):
        # imgName=str(self.count)+".jpg"
        # self.count+=1
        url=request.url
        imgName=url.split('/')[-1]
        return imgName

    #返回给下一个即将被执行的管道类
    def item_completed(self, results, item, info):
        return item

settings.py

单线程设置

USER_AGENT ='...'
ROBOTSTXT_OBEY = False
ITEM_PIPELINES = {
   'test3_2.pipelines.imgsPileLine': 300,
    #换成自定义的管道类
}
#图片存储目录
IMAGES_STORE='./images'

多线程设置:在settings.py中开启多线程,可自动设置线程数

终端输出图片url

查看图片

心得

1、多线程可以不使用threading,只需要设置CONCURRENT_REQUESTS值就行,而且scrapy框架是异步的,所以在下载图片时并不是按顺序下载的。

2、一开始有点疑惑,没有像之前商城或其它网页明显的翻页,如何实现爬取特定页数,再想想就可以知道爬取全部链接,限定爬取链接的数量即可

3、存取图片需要自定义一个类继承ImagesPipeline,在自定义类中实现根据图片地址进行图片数据请求并存储图片,和文本数据类型存储不一样

作业2

要求:

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

网站:http://quote.eastmoney.com/center/gridlist.html#hs_a_board

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

输出信息:

MySQL数据库存储和输出格式如下:表头英文命名例如:序号id,股票代码:bStockNo……,由同学们自行定义设计

序号 股票代码 股票名称 最新报价 涨跌幅 涨跌额 成交量 振幅 最高 最低 今开 昨收
1 688093 N世华 28.47 10.92 26.13万 7.6亿 22.34 32.0 28.08 30.20 17.55
2……

test.py

主要代码:

    #解析数据并将数据传入管道
    def parse(self, response):
        data = response.text
        json_data = json.loads(data[data.find('{'):data.rfind('}') + 1])
        stock_list = json_data['data']['diff']
        count=0
        for stock in stock_list:
            item = Test32Item()
            count+=1
            item['id']=count
            item['code'] = stock['f12']
            item['name'] = stock['f14']
            item['latest_price'] = stock['f2']
            item['change_degree'] = stock['f3']
            item['change_amount'] = stock['f4']
            item['count'] = stock['f5']
            item['money'] = stock['f6']
            item['zfcount'] = stock['f7']
            item['highest'] = stock['f15']
            item['lowest'] = stock['f16']
            item['today'] = stock['f17']
            item['yestoday'] = stock['f18']
            yield item

实现翻页操作

    #爬取3页数据
    if self.page_num <= 3:
            self.page_num += 1
            url = f'https://69.push2.eastmoney.com/api/qt/clist/get?cb=jQuery112404359196896638151_1697701391202&pn={self.page+1}&pz=20&po=1&np=1&ut=bd1d9ddb04089700cf9c27f6f7426281&fltt=2&invt=2&wbp2u=|0|0|0|web&fid=f3&fs=m:1+t:2,m:1+t:23&fields=f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f12,f13,f14,f15,f16,f17,f18,f20,f21,f23,f24,f25,f22,f11,f62,f128,f136,f115,f152&_=1697701391203'
            yield scrapy.Request(url=url, callback=self.parse)

items.py

class Test32Item(scrapy.Item):
    id = scrapy.Field()
    code = scrapy.Field()
    name = scrapy.Field()
    latest_price = scrapy.Field()
    change_degree = scrapy.Field()
    change_amount=scrapy.Field()
    count = scrapy.Field()
    money = scrapy.Field()
    zfcount = scrapy.Field()
    highest = scrapy.Field()
    lowest = scrapy.Field()
    today = scrapy.Field()
    yestoday = scrapy.Field()

pipelines.py

import pymysql

#将数据存取到mysql中
class mysqlPipeline:
    conn=None
    cursor=None
    
    #连接数据库
    def open_spider(self,spider):
        self.conn=pymysql.Connect(host='127.0.0.1',port=3306,user='root',password='123456',db='spiders',charset='utf8')
	
    #插入数据
    def process_item(self, item, spider):
        self.cursor=self.conn.cursor()
        sql = "INSERT INTO test32(序号,代码,名称,最新价,涨跌幅,涨跌额,成交量,成交额,振幅,最高,最低,今收,昨收)" \
                      " VALUES (%s,%s, %s, %s,%s, %s, %s, %s, %s, %s, %s, %s, %s)"

        try:
            self.cursor.execute(sql, [item["id"],item["code"],item["name"],item["latest_price"],item["change_degree"],item["change_amount"],item["count"],item["money"],item["zfcount"],item["highest"],item["lowest"],item["today"],item["yestoday"]])
            self.conn.commit()
            print("插入成功")
        except Exception as err:
            print("插入失败", err)
        return item
	
    #关闭数据库
    def close_spider(self,spider):
        if self.cursor:
            self.cursor.close()
        if self.conn:
            self.conn.close()

settings.py

USER_AGENT ='...'
ROBOTSTXT_OBEY = False
#开启管道
ITEM_PIPELINES = {
   'test32.pipelines.mysqlPipeline': 300,
}

终端查看是否插入数据成功

查看数据库

心得

1、这个是动态网页,在抓包时查看预览信息没看到数据,找了很久很久,然后我在抓包页面按ctrl+f搜索页面数据,找到对应数据的包,发现数据在预览信息中是很长一行,数据在很后面,而之前我恰好忽略了它导致找了很久。

2、知道了在scrap中爬取动态网站数据有好几种方法,可以手动抓包解析数据包获得数据,也可以结合selenium获取动态页面数据,如果直接对页面发送请求获取不到所需数据。

作业3

要求:

熟练掌握 scrapy 中 Item、Pipeline 数据的序列化输出方法;使用scrapy框架+Xpath+MySQL数据库存储技术路线爬取外汇网站数据。
候选网站:中国银行网:https://www.boc.cn/sourcedb/whpj/
输出信息:
Currency TBP CBP TSP CSP Time
阿联酋迪拉姆 198.58 192.31 199.98 206.59 11:27:14

...

test.py

    #解析数据
    def parse(self, response):
        count=0
        #获得tr列表
        # trs=response.xpath('/html/body/div[1]/div[5]/div[1]/div[2]/table/tbody/tr/td')        
        trs=response.xpath('//html/body/div/div[@class="BOC_main"]/div[1]/div[2]/table//tr')
        print(trs)
        print("-----------------------------")

        for tr in trs:
            currency = tr.xpath("./td[1]/text()").extract_first()
            tsp = tr.xpath("./td[2]/text()").extract_first()
            csp = tr.xpath("./td[3]/text()").extract_first()
            tbp = tr.xpath("./td[4]/text()").extract_first()
            cbp = tr.xpath("./td[5]/text()").extract_first()
            time = tr.xpath("./td[8]/text()").extract_first()
            count+=1
            #忽略第一行标题
            if count ==1 :
                continue
            item = Test33Item(currency=currency, tsp=tsp, csp=csp, tbp=tbp, cbp=cbp, time=time)
            print(currency,tsp,csp,tbp,cbp,time+"-------------------")
            yield item

items.py

class Test33Item(scrapy.Item):
    currency = scrapy.Field()
    tsp = scrapy.Field()
    csp = scrapy.Field()
    tbp = scrapy.Field()
    cbp = scrapy.Field()
    time = scrapy.Field()

pipelines.py

import pymysql
import scrapy
class mysqlPipeline:
    conn = None
    cursor = None

    def __init__(self):
        self.conn = None
        self.cursor = None


    def open_spider(self, spider):
        self.conn = pymysql.Connect(host='127.0.0.1', port=3306, user='root', password='123456', db='spiders',
                                    charset='utf8')

    def process_item(self, item, spider):
        self.cursor = self.conn.cursor()
        sql = "INSERT INTO test33(Currency,TBP,CBP,TSP,CSP,Time)" \
              " VALUES (%s,%s, %s, %s,%s, %s)"

        try:
            self.cursor.execute(sql,[item["currency"], item["tbp"], item["cbp"], item["tsp"], item["csp"], item["time"]])
            self.conn.commit()
            print("插入成功")
        except Exception as err:
            print("插入失败", err)
        return item

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

stetings.py

USER_AGENT ='...'
ROBOTSTXT_OBEY = False
ITEM_PIPELINES = {
   'test32.pipelines.mysqlPipeline': 300,
}

查看输出:

查看数据库内容:

心得

1、还是和往常一样右键打开检查,复制xpath路径,获取tr列表,解析数据,但是tr列表打印出来却一直是空的,而response是有内容的,在页面上xpath表达式也能定位到元素。

卡了很久依旧不知道咋回事,然后问助教才知道浏览器在对table进行处理时,自动添加了tbody元素,并且自动对div进行了排序,导致复制的xpath路径出错。我打开检查看到的代码是动态调试了的,而response返回的是网页源码,在源码上没有tbody,所以xpath解析不到数据,然后把tbody删了就能定位到元素了!!!又学到了一个解决bug的方法~

2、解析页面数据时要小心,如果得不到所需数据就多打印信息,查看哪步出错并找到解决方法。