【数据采集与融合技术】第四次大作业

【数据采集与融合技术】第四次大作业

「数据采集」实验四

一、作业①

1.1 题目

  • 要求:熟练掌握 scrapy 中 Item、Pipeline 数据的序列化输出方法;Scrapy+Xpath+MySQL数据库存储技术路线爬取当当网站图书数据

  • 候选网站:http://search.dangdang.com/?key=python&act=input

  • 关键词:学生可自由选择

  • 输出信息:MySQL的输出信息如下

1.2 代码及思路

settings:4/4.1/settings.py · 灰色/2019级数据采集与融合技术 - 码云 - 开源中国 (gitee.com)

items:4/4.1 · 灰色/2019级数据采集与融合技术 - 码云 - 开源中国 (gitee.com)

mySpider:4/4.1/mySpider.py · 灰色/2019级数据采集与融合技术 - 码云 - 开源中国 (gitee.com)

pipelines:4/4.1/pipelines.py · 灰色/2019级数据采集与融合技术 - 码云 - 开源中国 (gitee.com)

1.2.1 settings.py

设置是否遵循robot协议

# Obey robots.txt rules
ROBOTSTXT_OBEY = False

设置请求头

# Override the default request headers:
DEFAULT_REQUEST_HEADERS = {
  'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,'
            'image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
  'Accept-Encoding':'gzip, deflate',
  'Accept-Language':'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
  'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
               'AppleWebKit/537.36 (KHTML, like Gecko) '
               'Chrome/95.0.4638.69 Safari/537.36 Edg/95.0.1020.44'
}

打开pipelines

# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
   'dangdang.pipelines.DangdangPipeline': 300,
}

1.2.2 items.py

按照题目要求编写bookItem类,类中含有序号(id),书名(bTitle)等属性

class bookItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    id=scrapy.Field()
    bTitle=scrapy.Field()
    bAuthor=scrapy.Field()
    bPublisher=scrapy.Field()
    bDate=scrapy.Field()
    bPrice=scrapy.Field()
    bDetail=scrapy.Field()
    pass

1.2.3 mySpider.py

首先设置爬虫名,爬取域,带爬取页面的url列表start_urls如下

name = 'mySpider'
allowed_domains = ['search.dangdang.com']
start_urls = ['http://search.dangdang.com/?key=%CD%F5%D0%A1%B2%A8&act=input']

可以看到当当网是用get方法传递用户输入的关键词,且对中文进行了编码

先考虑单页的爬取

使用谷歌浏览器的插件Xpath-helper可以很方便的找到页面元素对应的Xpath路径,以第一本书为例

猜测"//ul/li"可以定位每本书,试验发现果真如此,但连搜索栏的排序按钮也被找到,因此还需要加上属性id,查找如下

可以看到确实定位到了每本书

获取所有书对应商品的集合

 data=response.text
 selector=scrapy.Selector(text=data)
 books=selector.xpath("//li[@id]")

之后便可以遍历该集合,在每个商品下爬取相应信息,以书名为例,用Xpath-helper选定书名元素,输出如下。

遍历集合获取所有商品信息,存入一个Item项中传给pipelines

            for book in books:
            item=bookItem()
            #序号
            item['id']=cnt
            #书名
            item['bTitle']=book.xpath("./p[@class='name']").xpath("string()").extract_first()   
            #作者
            item['bAuthor']=book.xpath("./p[@class='search_book_author']/span[1]/a[1]")///
            .xpath("string()").extract_first() 
            #出版社
            item['bPublisher']=book.xpath("./p[@class='search_book_author']/span[3]/a")///
            .xpath("string()").extract_first()
            #出版日期
            item['bDate']=book.xpath("./p[@class='search_book_author']/span[2]").xpath("string()").extract_first()
            item['bDate']=str(item['bDate']).replace('/','')
            #价格
            item['bPrice']=book.xpath("./p[@class='price']/span[@class='search_now_price']")///
            .xpath("string()").extract_first()
            #简介
            item['bDetail']=book.xpath("./p[@class ='detail']").xpath("string()").extract_first()
            
            yield item

1.2.4 pipelines.py

首先编写数据库类,有打开数据库,插入数据,关闭数据库三种方法

class BookDB:
    def openDB(self):
        self.con = sqlite3.connect("books.db") #建立数据库链接,若没有对应数据库则创建
        self.cursor = self.con.cursor() #建立游标
        try:
            self.cursor.execute("create table books "
                                "(Bid int(4),Btitle varchar(16),"
                                "Bauthor varchar(32),Bpublisher varchar(64),"
                                "Bdate varchar(32),Bprice varchar(8),Bdetail varchar(64))")
        except:
            self.cursor.execute("delete from books")

    def closeDB(self):
        self.con.commit()
        self.con.close()

    def insert(self, Bid, Btitle, Bauthor, Bpublisher, Bdate, Bprice, Bdetail):
        try:
            self.cursor.execute("insert into books(Bid, Btitle, Bauthor, Bpublisher, Bdate, Bprice, Bdetail) values (?,?,?,?,?,?,?)",
                                (Bid, Btitle, Bauthor, Bpublisher, Bdate, Bprice, Bdetail))
        except Exception as err:
            print(err)

接着设置oprn_spider方法,这个方法在scrapy项目第一次运行时会自动运行且仅运行一次,在该方法中创建数据库对象,打开数据库

def open_spider(self, spider):
	print("开始爬取")
	self.count = 1
	self.db = BookDB()
	self.db.openDB()

接着编写process_item方法,这个方法每当spider传递Item到pipelines时都会调用一次,在该方法中将传入商品信息存到数据库中

    def process_item(self, item, spider):
        self.db.insert(item['id'], item['bTitle'], item['bAuthor'], item['bPublisher'], item['bDate'], item['bPrice'],item['bDetail'])
        return item

最后编写close_spider方法,这个方法在scrapy关闭时会自动运行且仅运行一次,在该方法中关闭数据库对象

def close_spider(self, spider):
	self.db.closeDB()
	print("结束爬取")

1.3 运行结果

运行后数据库截图如下

1.4 心得体会

● scrapy不需要手动编写数据库类,可以直接在settings中设置连接的数据库

● 用浏览器插件找元素对应的xpath的规律会让你的爬取事半功倍

二、作业②

2.1 题目

  • 要求:熟练掌握 Selenium 查找HTML元素、爬取Ajax网页数据、等待HTML元素等内容;使用Selenium框架+ MySQL数据库存储技术路线爬取“沪深A股”、“上证A股”、“深证A股”3个板块的股票数据信息。

  • 候选网站:东方财富网:http://quote.eastmoney.com/center/gridlist.html#hs_a_board候选网站:东方财富网:http://quote.eastmoney.com/center/gridlist.html#hs_a_board

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

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

2.2 代码及思路

settings:4/4.2/settings.py · 灰色/2019级数据采集与融合技术 - 码云 - 开源中国 (gitee.com)

items:4/4.2/items.py · 灰色/2019级数据采集与融合技术 - 码云 - 开源中国 (gitee.com)

Myspiders:4/4.2/Myspider.py · 灰色/2019级数据采集与融合技术 - 码云 - 开源中国 (gitee.com)

pipelines:4/4.2/pipelines.py · 灰色/2019级数据采集与融合技术 - 码云 - 开源中国 (gitee.com)

2.2.1 settings.py

设置是否遵循robot协议

# Obey robots.txt rules
ROBOTSTXT_OBEY = False

打开pipelines

# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
   'cmb.pipelines.CmbPipeline': 300,
}

2.2.2 items.py

根据题目要求编写item类,类有序号(id),货币名(Currency)等属性

class Item(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    id=scrapy.Field()
    Currency=scrapy.Field()
    TSP=scrapy.Field()
    CSP=scrapy.Field()
    TBP = scrapy.Field()
    CBP = scrapy.Field()
    time=scrapy.Field()
    pass

2.2.3 Myspider.py

首先设置爬虫名,爬取域,带爬取页面的url列表start_urls如下

 name = 'Myspider'
 allowed_domains = ['fx.cmbchina.com/hq']
 start_urls = ['http://fx.cmbchina.com/hq/']

观察到"//tbody/tr"为每个货币对应的Xpath路径,但实际查询结果为空,

去掉tbody改为查询"//tr"才正确返回每个货币,可能的原因是tbody是页面加载时加载进html源码的而非本身自带的,接着开始爬取,这里直接采用下标访问tr元素,在循环中靠''"//table[@class='data']/tr[%d]" % cnt'遍历每种货币。

data = response.text
        selector = scrapy.Selector(text=data)
        for cnt in range(2, 12):
            item = Item()
            cy = selector.xpath("//table[@class='data']/tr[%d]" % cnt)
            #序号
            item['id'] = cnt - 1
            #名称
            item['Currency'] = cy.xpath("./td[@class='fontbold'][1]").xpath("string()").extract_first()
            item['Currency'] = str(item['Currency']).strip()
            #TSP
            item['TSP'] = cy.xpath("./td[@class='numberright'][1]").xpath("string()").extract_first()
            item['TSP'] = str(item['TSP']).strip()
            #CSP
            item['CSP'] = cy.xpath("./td[@class='numberright'][2]").xpath("string()").extract_first()
            item['CSP'] = str(item['CSP']).strip()
            #TBP
            item['TBP'] = cy.xpath("./td[@class='numberright'][3]").xpath("string()").extract_first()
            item['TBP'] = str(item['TBP']).strip()
            #CBP
            item['CBP'] = cy.xpath("./td[@class='numberright'][4]").xpath("string()").extract_first()
            item['CBP'] = str(item['CBP']).strip()
            #当前时间
            item['time'] = cy.xpath("./td[8]").xpath("string()").extract_first()
            item['time'] = str(item['time']).strip()
            yield item
            cnt += 1

注意.xpath("string()").extract_first()爬取下来的结果有空格,用str类的构造方法将scrapy.Field对象转换为字符串,即可调用字符串对应的函数处理结果

2.2.4 pipelines.py

首先编写数据库类,有打开数据库,插入数据,关闭数据库三种方法

class CurrencyDB:
    def openDB(self):
        self.con = sqlite3.connect("currencys.db") #建立数据库链接,若没有对应数据库则创建
        self.cursor = self.con.cursor() #建立游标
        try:
            self.cursor.execute("create table currencys "
                                "(Cid int(4),Ccurrency varchar(16),"
                                "Ctsp varchar(32),Ccsp varchar(64),"
                                "Ctbp varchar(32),Ccbp varchar(8),Ctime varchar(64))")
        except:
            self.cursor.execute("delete from currencys")

    def closeDB(self):
        self.con.commit()
        self.con.close()

    def insert(self, Cid, Ccurrency, Ctsp, Ccsp, Ctbp, Ccbp, Ctime):
        try:
            self.cursor.execute("insert into currencys(Cid, Ccurrency, Ctsp, Ccsp, Ctbp, Ccbp, Ctime) values (?,?,?,?,?,?,?)",
                                (Cid, Ccurrency, Ctsp, Ccsp, Ctbp, Ccbp, Ctime))
        except Exception as err:
            print(err)

接着设置oprn_spider方法,这个方法在scrapy项目第一次运行时会自动运行且仅运行一次,在该方法中创建数据库对象,打开数据库

def open_spider(self, spider):
	print("开始爬取")
	self.count = 1
	self.db = CurrencyDB()
	self.db.openDB()

接着编写process_item方法,这个方法每当spider传递Item到pipelines时都会调用一次,在该方法中将传入项信息存到数据库中

    def process_item(self, item, spider):
        self.db.insert(item['id'], item['Currency'], item['TSP'], item['CSP'], item['TBP'], 		 
        item['CBP'],item['time'])
        return item

最后编写close_spider方法,这个方法在scrapy关闭时会自动运行且仅运行一次,在该方法中关闭数据库对象

def close_spider(self, spider):
	self.db.closeDB()
	print("结束爬取")

2.3 运行结果

运行后数据库截图如下

2.4 心得体会

● 有的页面是动态加载的,直接用urllib或scrapy爬取到的只能是未装在内容的页面的html源码,这种动态页面用Selenium爬取较为合适

● Webelement.xpath("string()")方法爬取下来的结果仍然不是字符串,而是selector对象tag内的文本对应的selector对象,要调用相 应的extract_first方法之后再调用str类的才能获取字符串

●Xpath中也可以使用下标遍历元素

三、作业③

3.1 题目

  • 要求:熟练掌握 Selenium 查找HTML元素、爬取Ajax网页数据、等待HTML元素等内容;使用Selenium框架+ MySQL数据库存储技术路线爬取“沪深A股”、“上证A股”、“深证A股”3个板块的股票数据信息。

  • 候选网站:东方财富网:http://quote.eastmoney.com/center/gridlist.html#hs_a_board

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

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

3.2 代码及思路

4/4-3.py · 灰色/2019级数据采集与融合技术 - 码云 - 开源中国 (gitee.com)

3.2.1 设置浏览器设置

#设置启动时浏览器不可见
from selenium.webdriver.common.by import By

chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')

3.2.2 创建模拟浏览器

#创建chrome浏览器
driver= webdriver.Chrome()
#driver= webdriver.Chrome(chrome_options=chrome_options)

3.2.3 观察三个板块,找出url规律

沪深A股

上证A股

深证A股

发现url都为'http://quote.eastmoney.com/center/gridlist.html#[...]_a_board'

其中[...]为板块的拼音缩写

创建url列表

urls=["http://quote.eastmoney.com/center/gridlist.html#hs_a_board",
      "http://quote.eastmoney.com/center/gridlist.html#sh_a_board",
      "http://quote.eastmoney.com/center/gridlist.html#sz_a_board"]
bks=["沪深A股","上证A股","深证A股"]

之后遍历该列表进行爬取,以沪深A股为例

3.2.4 翻页逻辑

Selenium中的翻页即找到翻页按钮并模拟点击,用Xpath-helper查找如下

翻页逻辑如下

driver.find_element(By.XPATH, "//a[@class ='next paginate_button']").click()

3.2.5 爬取一个板块的三个页面

以一支股票为单位爬取,对应一个'tr'tag

	#爬取三页
    cnt=0
    for page in range(3):
        time.sleep(1)
        for i in range(1,21):
            cnt+=1
            s=""
            if i%2==1:
                s="'odd'"
            else:
                s="'even'"
            dt=driver.find_element(By.XPATH,"//tr[@class=%s][%d]"%(s,int((i+1)/2)))
            id=cnt
            bk=bks[k]
            code=dt.find_element(By.XPATH,"./td[2]").text
            name=dt.find_element(By.XPATH,"./td[@class='mywidth']/a").text
            newest_price=dt.find_element(By.XPATH,"./td[@class='mywidth2'][1]/span[@class]").text
            zdf=dt.find_element(By.XPATH,"./td[@class='mywidth2'][2]/span[@class]").text
            zde=dt.find_element(By.XPATH,"./td[7]/span[@class]").text
            cjl=dt.find_element(By.XPATH,"./td[8]").text
            cje=dt.find_element(By.XPATH,"./td[9]").text
            zf=dt.find_element(By.XPATH,"./td[10]").text
            zg=dt.find_element(By.XPATH,"./td[11]/span[@class]").text
            zd=dt.find_element(By.XPATH,"./td[12]").text
            jk=dt.find_element(By.XPATH,"./td[13]/span[@class]").text
            zs=dt.find_element(By.XPATH,"./td[14]").text
            db.insert(bk, id, code, name, newest_price, zdf, zde, cjl, cje, zf, zg, zd, jk, zs)
        #翻页
        if i!=2:
            driver.find_element(By.XPATH, "//a[@class ='next paginate_button']").click()

3.3 运行结果

运行后数据库截图如下

3.4 心得体会

●Selenium可以实现模拟浏览器的输入,点击等操作,但仍然无法绕过人机验证

●Selenium的翻页逻辑十分简单,只需要找到翻页按钮并点击即可

posted @ 2021-11-13 22:28  暴走小铸币  阅读(90)  评论(0编辑  收藏  举报