数据采集第三次实验
作业①:
- 要求:指定一个网站,爬取这个网站中的所有的所有图片,例如中国气象网
。分别使用单线程和多线程的方式爬取。(限定爬取图片数量为学号后4位)
- 输出信息:
将下载的Url信息在控制台输出,并将下载的图片存储在weather子文件中,并给出截图。
(1)爬取中国气象网网页内容
单线程实验过程:
1.先通过主url,检查页面,获取到该页面下存在的所有a[href]链接,全部存下,后续使用
获取类似的link,确保图片数量。
(代码如下)start_url = "http://www.weather.com.cn" header = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 Edg/94.0.992.38"} r = requests.get(start_url, headers=header) r.raise_for_status() r.encoding = r.apparent_encoding data = r.text soup = BeautifulSoup(data, "html.parser") # 解析html a = '<a href="(.*?)" ' linklist = re.findall(re.compile(a), str(soup)) # 正则表达式获取link
2.定义封装一个获取各个url下图片链接的函数,得到以下代码:
def imagelist(link): header = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 Edg/94.0.992.38"} r = requests.get(link, headers=header) r.raise_for_status() r.encoding = r.apparent_encoding data = r.text soup = BeautifulSoup(data, "html.parser") images = soup.select("img") p = r'img.*?src="(.*?)"' imagelist = [] str1 = re.findall(p, str(images), re.S) # # print(str1) for i in range(len(str1)): if str1[i] not in imagelist: imagelist.append(str1[i])if str1[i] else "" # print(imagelist) # print(len(imagelist)) return imagelist
3.定义一个下载图片的函数(如下)
def download(link): global count file = "E:/weather/" + "第" + str(count+1) + "张.jpg" # file指先在指定文件夹里建立相关的文件夹才能爬取成功 print("第" + str(count+1) + "张爬取成功") count += 1 urllib.request.urlretrieve(url=link, filename=file)
4.运行结果:
多线程实验过程:
1.与单线程实验类似,运用多线程加快加载速度。
首先解析html,同样的获取多个linkstart_url = "http://www.weather.com.cn" headers= { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 Edg/94.0.992.38"} r = requests.get(start_url, headers=headers) r.raise_for_status() r.encoding = r.apparent_encoding data = r.text soup = BeautifulSoup(data, "html.parser") linklist = [] a = '<a href="(.*?)" ' linklist1 = re.findall(re.compile(a), str(soup)) for i in range(len(linklist1)): if i < 20: linklist.append(linklist1[i]) if linklist1[i] != "javascript:void(0);" else "" # print(len(linklist)) # 到此处便获取了所有的link threads=[] for i in linklist: imageSpider(i) # 将link调用回imageSpider函数
2.获取单个页面内的所有img链接
soup=BeautifulSoup(data,"html.parser") images=soup.select("img") for image in images: try: src=image["src"] url=urllib.request.urljoin(start_url,src)
这里获取到的图片链接是img下的src,但是由于网站原因当中混入了http://www.weather.com.cn此网站,因此这里做下判断:
if url not in urls: if not str(url).endswith('.cn'):
3.最后封装download函数,用于多线程爬取:这里先对每个图片url进行处理,获取每个图片链接的后四位,即图片的格式:最后写入文件
def download(url,count): try: if(url[len(url)-4]=="."): type=url[len(url)-4:] else: type="" req=urllib.request.Request(url,headers=headers) data=urllib.request.urlopen(req,timeout=100) data=data.read() fobj=open("E:\images\\"+str(count)+type,"wb") fobj.write(data) fobj.close() print("downloaded "+str(count)+type+"\n") except Exception as err: print(err)
4.多线程设定:
T=threading.Thread(target=download,args=(url,count)) T.setDaemon(False) T.start() threads.append(T)
for t in threads: t.join() print("The End")
5.运行结果:
心得体会:
通过本次实验首先复习了从网站上下载图片的基本操作,以及如何使用多线程来加快下载速度。本次实验不同的地方在于不在同一个单一页面内爬取图片,先要通过主url,找到他的所有分支,在对限定数量的图片进行下载。总体完成情况较好,也较熟练地掌握了。
作业②:
- 要求:使用scrapy框架复现作业①。 输出信息: 同作业①
- 要求:爬取豆瓣电影数据使用scrapy和xpath,并将内容存储到数据库,同时将图片存储在imgs路径下。 输出信息:
我的Gitee(2)利用scrapy框架下载网站图片
实验过程:
1.首先创建scrapy项目2.编写myspiders,创建myspiders类,定义爬虫名称以及start_urls
class myspides(scrapy.Spider): # 设定基础参数 name = 'myspiders' start_urls = ["http://www.weather.com.cn"]
2.同作业1,定义一个获取图片链接的函数
def image(self,link): header = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 Edg/94.0.992.38"} r = requests.get(link, headers=header) r.raise_for_status() r.encoding = r.apparent_encoding data = r.text soup = bs4.BeautifulSoup(data, "html.parser") images = soup.select("img") p = r'.+?src="(\S+)"' imagelist = [] str1 = re.findall(p, str(images), re.S) # # print(str1) for i in range(len(str1)): if str1[i] not in imagelist: imagelist.append(str1[i]) if str1[i] else "" # print(imagelist) # print(len(imagelist)) return imagelist
3.在parse函数中同样的获取a[href]
a = soup.select("a[href]") links = [] for link in a: links.append(link["href"]) if link["href"] != 'javascript:void(0)' else ""
4.之后便进行图片的下载:
for i in images: item["images"]= i item["name"] = "E:/weather/" + str(count+1) + ".jpg" urllib.request.urlretrieve(item["images"], filename=item["name"]) count += 1 if count <=112: break yield item
5.在item.py中,定义myspiders中运用到的item项,这里定义item["images"],item["name"]
6.在setting.py文件中进行设定:
(注意这里要忽略网站爬虫协议)ROBOTSTXT_OBEY = False
在对其他设定:
运行结果
心得体会
该实验的主要爬虫思路,和作业①一致,主要是要求用scrapy框架进行实验,估计老师是为了让我们熟练使用scrapy框架,经过这次实验也确实很熟练了,详细原因见实验三。
作业③:
我的Gitee序号 电影名称 导演 演员 简介 电影评分 电影封面 1 肖申克的救赎 弗兰克·德拉邦特 蒂姆·罗宾 希望让人自由 9.7 ./imgs/xsk.jpg 2.... (3)爬取豆瓣相关信息
实验过程:
1.先对豆瓣网页进行观察,可以发现需要爬取的信息都分布在页面之中:但是这里发现了一个问题对于要爬取的演员名单,在该页面下并不完整,导演名称较为混乱,为了完善该爬虫,这里决定在外部界面爬取该部分链接,进入该link,对演员信息进行爬取。
2.分析完网页接着就用scrapy框架,对该网站进行爬取。首先创建scrapy项目。
3.编写主函数myspiders,创建myspides(scrapy.Spider):定义爬虫名和start_urls
name = 'myspiders' allowed_domains = ['movie.douban.com'] start_urls = ['https://movie.douban.com/top250?start=0']
4.封装parse(self, response)函数,解析html,利用xpath对数据进行查询
title = li.xpath("./div[@class='pic']/a/img/@alt").extract_first() img = li.xpath("./div[@class='pic']/a/img/@src").extract_first() score = li.xpath("./div[@class='info']/div[@class='bd']/div[@class='star']/span[position()=2]/text()").extract_first() rank = li.xpath("./div[@class='pic']/em/text()").extract_first() comment = li.xpath("./div[@class='info']/div[@class='bd']/p[position()=2]/span/text()").extract_first()
5.对图片的下载处理:
file = "E:/movie/" + str(item["title"]) + ".jpg" # file指先在指定文件夹里建立相关的文件夹才能爬取成功 urllib.request.urlretrieve(item["img"], filename=file) item["file"] = file.strip()
6.继续利用xpath爬取该电影栏目的链接,进入该链接爬取导演,演员名称
link = li.xpath("./div[@class='pic']/a/@href").extract_first() soup = bs4.BeautifulSoup(r.text, "html.parser") title = soup.select("div[id='info']") reg = r'<a href=".*?" rel=".*?">(.*?)</a>' actor = re.findall(reg,str(title)) # print(actor[0]) act = soup.select("span[class='actor'] span[class='attrs']")[0].text # print(act) item["actor"] = actor[0] if actor else "" item["star"] = act if act else "" yield item
7.最后对myspiders.py进行翻页处理:
page = selector.xpath( "//div[@class='paginator']/span[@class='thispage']/following-sibling::a[1]/@href").extract_first() print(page) link_nextpage = "https://movie.douban.com/top250" + str(page) if page: url = response.urljoin(link_nextpage) yield scrapy.Request(url=url, callback=self.parse, dont_filter=True)
8.接着对item.py文件进行处理,定义所需的items类
class DemoItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() title = scrapy.Field() img = scrapy.Field() link = scrapy.Field() score = scrapy.Field() comment = scrapy.Field() rank = scrapy.Field() actor = scrapy.Field() star = scrapy.Field() file = scrapy.Field() pass
9.对settings要进行处理,一开始我只设置了'demo.pipelines.DemoPipeline': 300,发现无论怎么爬取都没有内容,苦苦寻找原因最后发现因为没有忽略网站爬虫协议,所以做了如下修改:
ROBOTSTXT_OBEY = False USER_AGENT = 'demo (+http://www.yourdomain.com)'
10.对pipelines进行编写,这里主要是做输出处理和存入sqlite数据库,这里创建数据库表
if flag: self.con = sqlite3.connect("movie.db") self.cursor = self.con.cursor() try: self.cursor.execute( "create table movie (int Mrank ,Mtitle varchar(256),Mactor varchar(256),Mstar text,Mcomment varchar(256),Mscore varchar(256),Mfile varchar(256),constraint pk_movie primary key (Mrank,Mtitle))") flag = False except: self.cursor.execute("delete from movie")
或许可以将数据插入MySQL
# def open_spider(self, spider): # print("opened") # try: # self.con = pymysql.connect(host="127.0.0.1", port=3306, user="root", passwd="chen961122", db="test", # charset="utf8") # self.cursor = self.con.cursor(pymysql.cursors.DictCursor) # self.cursor.execute("delete from movies") # self.opened = True # except Exception as err: # print(err) # self.opened = False # # def close_spider(self, spider): # if self.opened: # self.con.commit() # self.con.close() # self.opened = False # print("closed")
插入数据
# sqlite self.cursor.execute("insert into movie (Mrank,Mtitle,Mactor,Mstar,Mcomment,Mscore,Mfile) values (?,?,?,?,?,?,?)", (item["rank"], item["title"],item["actor"],item["star"], item["comment"], item["score"], item["file"])) # mysql self.cursor.execute("insert into movies (Mrank,Mtitle,Mactor,Mstar,Mcomment,Mscore,Mfile) values (%s,%s,%s,%s,%s,%s,%s)", (item["rank"], item["title"],item["actor"],item["star"], item["comment"], item["score"], item["file"]))
这里第一次插入数据到数据库时,惊讶的发现数据少了几条,最后发现原来设定Mstar varchar(256)
爬取到的部分数据太长导致无法插入,最后设定为Mstar text,解决了该问题。运行结果
可以看见演员名单以及全部爬取下来了。
心得体会
在完成这项作业的过程中面临了多次网站异常,无法进入的问题,可能是爬取次数太多,
以后一定先缓存页面的html,但是这题缓存的话link内的大量网页都要存,不太现实,(这里在线提问,如何防止被封)在进行多次测试,在这次爬虫第一次接触了较为复杂的逻辑再次进入一个页面爬,感觉以后学了selenium,对于这类问题的处理应该没有这么麻烦。其次就是对scrapy越来越熟练,感觉爬虫还是一项需要经常练习的技术,这次也使用了mysql和sqlite两种数据库对数据进行存储,感觉大同小异,最后感谢吴玲老师在实践课的指导,不然卡在某些debug部分就出不来了。。。
我的Gitee(单)
我的Gitee(多)