数据采集与融合技术第三次作业
数据挖掘第三次实践
作业一
天气图片爬取实验
作业内容
指定一个网站,爬取这个网站中的所有的所有图片,例如中国气象网(http://www.weather.com.cn)。分别使用单线程和多线程的方式爬取。(限定爬取图片数量为学号后3位)
实践过程
-
获取网页中可访问链接
网页链接可通过xpath中提取a标签下的href属性值找到
但是为了防止提取到部分不可访问的内容所以对匹配到的值进行一个判断:
def getWebUrl(html):
""" 对传入html获取页面所有链接
:param html: 网页文本值
:return: 网页内包含的url列表
"""
e = etree.HTML(html)
# 利用xpath获取所有a标签下href属性值
urls = e.xpath("//a/@href")
url = []
for i in range (len(urls)-1):
# 判断获取的是否为网页链接
if(urls[i][0:4]=="http"):
url.append(urls[i])
return url
-
获取网页中所有图片链接
图片链接可通过img便签下src属性值找到
值得注意的是,本次获得的图片链接是不需要在前面加上http,直接保存就可以了
def getImgUrl(html):
""" 对传入html获取页面所有链接
:param html: 网页文本值
:return: 网页内包含的图片url列表
"""
e = etree.HTML(html)
# 利用xpath获取所有img标签下src属性值
urls = e.xpath("//img/@src")
url = []
for i in range(len(urls) - 1):
# 判断获取的是否为合法网页链接
if (urls[i][0:4] == "http"):
url.append(urls[i])
return url
-
对获取图片链接进行下载
图片下载至本地的思路就是:读取二进制信息,写入本地二进制文件即可
def downloadImg(urls): """ 下载图片并保存至本地 :param urls: 图片链接列表 """ for url in urls: img_content = requests.get(url).content name = url.split('/')[-1] with open('images/'+name,'wb') as f: f.write(img_content) f.close() print(name,"保存成功")
-
接下来是线程设计部分
本次采用每十个链接分配一个页面访问线程,每一个页面分配一个页面图片下载线程
具体实现方向是通过创建线程类来实现
页面访问线程:
class getImgs(threading.Thread): """ 网页下载图片子线程""" def __init__(self,urls): threading.Thread.__init__(self) self.urls = urls def run(self) : for url in self.urls: # 访问网页 html = getHtmlByRequests(url) print("正在爬取",url) # 提取图片链接 imgUrl = getImgUrl(html) # 启动下载图片子线程 dli = downLoad(imgUrl) dli.start()
图片下载线程:
class downLoad(threading.Thread): """ 图片下载至本地子线程""" def __init__(self,imgUrl): threading.Thread.__init__(self) self.urls = imgUrl def run(self): downloadImg(self.urls)
作业结果
心得体会
通过单线程和多线程之间的对比,发现多线程对爬取速度有很大的提升
作业二
天气图片爬取实验(scrapy框架)
作业内容
使用scrapy框架复现作业①。
实践过程
思路与上一题相似,主要就是如何转换为scrapy框架的问题
我的思路是分为两个parse,第一个parse用于提取页面信息
def parse(self, response):
""" 获取网页内包含链接
:param response: 访问页面结果
:yield: 对网页链接进行请求
"""
urls =[]
urls.extend(response.xpath("//a/@href").extract())
url = []
for i in range(len(urls) - 1):
if (urls[i][0:4] == "http"):
url.append(urls[i])
url = url[:40]
for u in url:
yield Request(url=u, callback=self.imgparse)
第二个parse用于获取图片链接
def imgparse(self,response):
""" 获取网页内图片链接
:param response: 访问页面结果
:yield: 图片链接存储至item
"""
imgurls = []
imgurls.extend(response.xpath('//img/@src').extract())
for url in imgurls:
if(url[:4]=="http"):
item = ImgcrawlItem()
item["url"] = url
yield item
作业结果
心得体会
scrapy框架相较于requests等方法结构更加清晰,不同部分的功能明确,一些简单的功能已经封装完成,实现较为方便,而且爬取速度真的很快。
作业三
豆瓣电影信息实验
作业内容
爬取豆瓣电影数据使用scrapy和xpath,并将内容存储到数据库,同时将图片存储在imgs路径下。
实践过程
-
首先是研究翻页规律,豆瓣还是很简单的就是通过一个start参数进行控制
base_url = "https://movie.douban.com/top250?start=" for i in range(1,10): page = str(i * 25) + "&filter=" url = base_url + page yield Request(url=url, callback=self.parse)
-
接着就是利用xpath插件对需要的信息进行提取
以提取排名为例子:
转换为代码:
ranks = response.xpath("//div[@class='pic']/em/text()").extract()
其他信息与上述寻找方式相同。
-
接着是实现pipeline,本题需要用两个数据处理,一是图片下载,二是数据写入至数据库
图片下载部分代码
class SaveImagePipeline(ImagesPipeline): def get_media_requests(self, item, info): yield Request(url=item['posterurl'],meta={'item':item}) # 设置图片存储路径及名称 def file_path(self, request, response=None, info=None): item = request.meta['item'] print('########', item) image_name = str(item['rank'])+item['name']+".jpg" # 图片存储形式:图片类型/图片名称.jpg return image_name def item_completed(self, results, item, info): return item
数据库写入部分代码
class SavetoSql(object):
def __init__(self):
self.connect = pyodbc.connect(
'DRIVER={SQL Server};'
'SERVER=(local);DATABASE=test;'
'Trusted_Connection=yes')
self.cur = self.connect.cursor()
def process_item(self, item,spider):
self.cur.execute(
'insert into movie([ranknum],[name], [director],[actor],[info],[score],[posterlocal]) values(?,?,?,?,?,?,?)',
(item['rank'],item['name'],item['director'],item['actor'],item['info'],item['score'],item['posterlocal'])
)
self.connect.commit()
return item
def close_spider(self, spider):
self.connect.close();
第一次爬取发现以下问题:
- 数据库消息是重复的
-
排名是文本形式,不好进行排序
-
有些信息存储错误
- 未被获取的信息
查看网页发现,原来是超长的导演名字导致re字符串匹配有问题
- 明显不匹配的电影名称和简介
才发现原来有的电影竟然没有简介
- 以及爬取信息不完整,只爬到244就结束了
以下是改进方案:
-
首先是re匹配改进:
仔细思考后发现,其实有可能为空值的只有演员这一栏,所以将字符串匹配空值判断变为:当无法匹配到
r'导演:(.*?)主'
时,就认为一整个字符串均为导演信息然后是解决部分电影无简介的问题:
infos = response.xpath("//div[@class='info']/div[@class='bd']/p[@class='quote']/span/text()").extract_first()
代码从.extract(),修改至.extract_first()即可,此时如果没有匹配到,会返回None值
-
接着就是比较麻烦的关于数据库的问题了
经过查询主要是因为scrapy是多线程,而写数据库速度较慢,有时候数据还来不及写进数据库就新的数据覆盖了,所以想到一个很简单的办法,给数据库的电影名加上主键约束!
接着解决爬取信息不完整的问题,主要还是因为scrapy多线程的问题,所以在setting里设置
AUTOTHROTTLE_ENABLED = True
可以动态控制爬取时间,重新爬取就可以解决以上问题了。
作业结果
心得体会
在爬虫大框架掌握的时候,有时候要对爬取信息进行核对,可能会发现许多小漏洞和可以优化的地方。