【数据采集与融合技术】第四次大作业
【数据采集与融合技术】第四次大作业
「数据采集」实验四
一、作业①
1.1 题目
-
要求:熟练掌握 scrapy 中 Item、Pipeline 数据的序列化输出方法;Scrapy+Xpath+MySQL数据库存储技术路线爬取当当网站图书数据
-
关键词:学生可自由选择
-
输出信息: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的翻页逻辑十分简单,只需要找到翻页按钮并点击即可