喜欢再看,养成好习惯Python版本3.8.0,开发工具:前面写的Pycharm到目前为止,爬虫你应该已经知道了三个基本部分:原理爬虫以及爬虫的两种实现方式。通过BeautifulSoup解析网页源码。如果以上三个部分还有问题,可以点击返回查看。作为基础内容,并不要求每个人都必须掌握,尤其是第三部分,网页分析的用法非常多,一般人很难全部记住。在写这篇文章的时候,我会时不时回头看看之前的文章。有些方法可能不是最简单的方法,但是只要达到目的就ok了。在这里你可以自由发挥。“小兄弟,这里为什么要用find的方式解析?可以用正则表达式吗?”“当然,也许你的正则表达式更容易实现。”然后,作为第一个小项目,我会尽可能把代码的每一步解释清楚。就算我不说也会有评论的,不用担心跟不上。另外,虽然是第一篇爬虫文章,但我还是会对爬虫的结果进行数据分析。对于项目来说比较简单,目的是让大家了解整个分析过程。记住一件事:爬虫永远不是我们的终点,最多是我们数据分析道路上的踏板。文末明确要求源码获取方式。我们今天要爬取的数据是豆瓣电影Top250。是的,只有250个数据。你猜对了。输入网址https://movie.douban.com/top250,我们可以看到网页是这样的:250条数据清晰,没有问题。可以看到这个页面其实包含了电影的主要内容:电影名称、排序、编剧、主演、年份、类型、评论数、评分,基本上都在这个页面里。但是我点开详细视频后发现:好像这个页面的资料比较全。我们要爬取什么数据,数据越多越好。比起这个详细的内容,每个星级的影评比较多,所以一定要选。好吧,让我们理清思路。首先进入豆瓣电影Top250,共10页,每页25部电影。然后,对于25部电影的每一页,进入其详细内容页面最后,分析每部电影的详细内容,将内容保存到数据库并编写伪代码#遍历10页data_movies#保存所有电影数据集forper_pageinpages:#爬取每页10页的数据movies=crawl_page_info(per_page)#遍历每页25部电影formovieinmovies:#爬取每部影片的详细内容data_per_movie=crawl_detail_info(movie)#将每部影片信息保存到数据集data_movies.append(data_per_movie)#将结果保存到数据库data_movies_to_mysql稍微解释一下:两层循环,第一层是遍历10页网页,因为每个网页分别有25部电影,所以,第二层循环依次遍历25部电影获取详细信息,最后将结果存入数据库!是不是很简单!但是在实践中你可能会遇到各种各样的问题,所以要做好心理准备!首先确定我们要输出的电影字段。主要数据包括:电影排序、电影名称、电影导演、电影编剧、电影主演、电影别名、电影链接关键数据包括:电影类型、制片国家、电影语言、上映日期、电影时长核心数据包括:电影评分,评论人数,5/4/3/2/1星对应评论比例字段如下:typemovie_country:电影国家movie_language:电影语言movie_release_date:电影上映日期movie_run_time:电影长度movie_second_name:电影又名movie_imdb_href:电影IMDb链接movie_rating:电影总评分movie_comments_user:电影评论人数movie_five_star_ratio:电影的5星比例movie_four_star_ratio:电影mov的4星比例ie_three_star_ratio:电影的3星比movie_two_star_ratio:电影的2星比movie_one_star_ratio:电影的1星比movie_note:电影的备注信息,一般为空然后开始主流程,确认主要参数,起始页码(默认为0),每个视频有25页,共10页,参数如下:start_page:起始页码page_size:每页大小pages:总页码classobject这里我们将每一部电影封装成一个对象,传入我们的主要参数,设置爬虫Head,并与数据库建立相关连接。类定义对象如下:classDouBanMovie:def__init__(self,url,start_page,pages,page_size):"""初始化@paramurl:抓取主网址@paramstart_page:起始页码@parampages:总页数number(截止页码)@parampage_size:每页的大小"""self.url=urlself.start_page=start_pageself.pages=pagesself.page_size=page_sizeself.data_info=[]self.pymysql_engine,self.pymysql_session=connection_to_mysql()self.headers={'User-Agent':'Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/69.0.3497.100Safari/537.36'}“小兄弟,你这里用的是什么数据库连接,我不明白为什么?”"不用着急,以后我会专门写一篇关于SQLAlchemy的数据库相关操作的文章#创建基类,Base=declarative_base()defconnection_to_mysql():"""连接数据库@return:"""engine=create_engine('mysql+pymysql://username:passwd@localhost:3306/db_name?charset=utf8')Session=sessionmaker(bind=engine)db_session=Session()#创建数据表Base.metadata.create_all(engine)returnengine,db_session判断主机:#当前页码小于0,异常退出ifself.start_page<0:return""#如果起始页大于总页数,self退出.#下一页self.start_page=self.start_page+1拼接当前页的url这里解释一下,当我们去的时候你访问第一页,你发现URL如下:https://movie.douban.com/top250当你访问下一页,你发现URL变化如下:https://movie.douban。com/top250?start=25&filter=然后下载一个页面的url变化如下:https://movie.douban.com/top250?start=50&filter=可以发现新的url只改变了后面的start参数,所以我们拼接出每个页面的url:start_number=self.start_page*self.page_sizenew_url=self.url+'?start='+str(start_number)+'&filter='抓取第一个页面确定主框架后,我们需要抓取第一个网页,即包含25个视频的页面这时候我们前面三节提到的爬虫实现方式就直接接过来了:self.headers={'User-Agent':'Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/69.0.3497.100Safari/537.36',}#抓取当前页码数据电影超链接跳转到详情页。通过谷歌浏览器F12开发者工具查看网页源码,可以看到每部电影的详细信息都在一个li标签中,每个li标签都有一个class='pic'的div,有这样的div中的一个a标签,这个a标签的href就是我们需要确定超链接位置的详细页面信息的超链接。打开我们上一节对BeautifulSoup的详解,定位分析soup=BeautifulSoup(response.text,'html.parser')#定位每部电影的div(标记为pic的div)soup_div_list=soup.find_all(class_="pic")#遍历获取soup_div_list中soup_div每个div的电影详情链接:#定位每个电影的a标签soup_a=soup_div.find_all('a')[0]movie_href=soup_a.get('href')print(movie_href)获取当前页超链接中25部电影的详细信息我们离成功又近了一步!抓取详情页也是一样,一行代码获取页面数据'''抓取页面获取详情页'''response=requests.get(url=movie_detail_href,headers=self.headers)创建有序字典tosavethecurrentmovieData#生成有序字典并保存电影resultmovie_info=OrderedDict()I让我们看看这个页面的源代码是什么样子的。首先是电影排序和电影名称,我们可以从上一页传递过来。不过,既然来了,是不是可以直接解析呢?必须这样做!这个比较简单,电影排名直接定位class='top250-no'的一个span标签,电影名定位property='v:itemreviewed'的一个span标签,获取标签内容。#解析电影排名和名称movie_info['movie_rank']=soup.find_all('span',class_='top250-no')[0].stringmovie_info['movie_name']=soup.find_all('span',property='v:itemreviewed')[0]。接下来的字符串是电影的主数据:这时候我们需要先定位到id='info'的div,然后我们可以看到整个div的数据就是我们需要的主数据。#定位到视频的divsoup_divdata=soup.find(id='info')"不是,小弟我发现有时候是一个编剧,有时候是多个编剧,当有多个编剧的时候,存在于多个跨度标签我该怎么办?”“很简单,我写个小函数统一处理。”defget_mul_tag_info(self,soup_span):"""将多个tag的结果合并成一个结果返回,用/分隔"""info=''forsecond_spaninsoup_span:#区分href和tag内容info=(''if(info=='')else'/').join((info,second_span.string))returninfo"对了,记得把最外面的span标签给我就行了。像这样:"#解析电影上映信息movie_info['movie_director']=self.get_mul_tag_info(soup_div.find_all('span')[0].find_all('a'))movie_info['movie_writer']=self.get_mul_tag_info(soup_div.find_all('span')[3].find_all('a'))movie_info['movie_starring']=self.get_mul_tag_info(soup_div.find_all('span')[6].find_all('a'))movie_info['movie_type']=self.get_mul_tag_info(soup_div.find_all('span',property='v:genre'))movie_info['movie_country']=soup_div.find(text='Country/regionofproduction:').next_element.lstrip().rstrip()movie_info['movie_language']=soup_div.find(text='language:').next_element.lstrip().rstrip()movie_info['movie_release_date']=self.get_mult_tag_info(soup_div.find_all('span',property='v:initialReleaseDate'))movie_info['movie_run_time']=self.get_mul_tag_info(soup_div.find_all('span',property='v:runtime'))movie_info['movie_imdb_href']=soup_div.find('a',target='_blank')['href']"小兄弟,又出问题了,有些视频没有别名s标签,如何处理?”“这个我们来做一下异常检测,如果没有,就手动赋一个null值。“movie_second_name=''try:movie_second_name=soup_div.find(text='aka:').next_element.lstrip().rstrip()exceptAttributeError:print('{0}hasnoaka'.format(movie_info['movie_name']))movie_info['movie_second_name']=movie_second_name最后还剩下评分数据,评分数据不仅有总评分,还有每个明星的评分。“小义哥,你看我们取的是哪些数据?”“孩子做选择,我当然要全部!”可以看到,总分和总评论数分别有一个唯一的属性,分别是property='v:average'的strong标签和property='v:votes'的span标签就ok了,那么得到直接获取数据:#获取总评分和总评论人数movie_info['movie_rating']=soup.find_all('strong',property='v:average')[0].stringmovie_info['movie_comments_user']=soup.find_all('span',property='v:votes')[0].string最后将每颗星的得分进行比较,可以看到5星/4星/3星/2星/1星对应分别是recommended/recommended/ok/poor/poor,可以看到它们都存在于一个class='ratings-on-weight'的div中,首先定位div:#locatethedivsoup_div=soup.find('div',class_="ratings-on-weight")然后获取每个星级的评分比例数据:#获取每个星级的评分movie_info['movie_five_star_ratio']=soup_div.find_all('div')[0].find(class_='rating_per').stringmovie_info['movie_four_star_ratio']=soup_div.find_all('div')[2].find(class_='rating_per').stringmovie_info['movie_three_star_ratio']=soup_div.find_all('div')[4].find(class_='rating_per').stringmovie_info['movie_two_star_ratio']=soup_div.find_all('div')[6].find(class_='rating_per').stringmovie_info['movie_one_star_ratio']=soup_div.find_all('div')[8].find(class_='rating_per').string打印出来看看我们当前的电影数据:对于movie_starring字段,只有输出部分显示OrderedDict([('movie_rank','No.1'),('movie_name','肖申克的救赎'),('movie_director','FrankDarabont'),('movie_writer','FrankDarabont/StephenKing'),('movie_starring','TimRobbins/MorganFreeman/BobGunton/WilliamSadler/),('movie_type','剧情/犯罪'),('movie_country','美国'),('movie_language','英语'),('movie_release_date','1994-09-10(多伦多电影节)/1994-10-14(美国)'),('movie_run_time','142分钟'),('movie_imdb_href','https://www.imdb.com/title/tt0111161'),('movie_rating','9.7'),('movie_comments_user','1720706'),('movie_five_star_ratio','84.8%'),('movie_four_star_ratio','13.6%'),('movie_three_star_ratio','1.4%'),('movie_two_star_ratio','0.1%'),('movie_one_star_ratio','0.1%'),('movie_note','')])大功告成,成功获取到想要的数据,最后一步:保存数据库#保存当前电影信息self.data_info.append(movie_info)#获取数据并保存为DataFramedf_data=pd.DataFrame(self.data_info)#将数据导入到mysql中,df_data.to_sql('t_douban_movie_top_250',self.pymysql_engine,index=False,if_exists='append')看看我们的数据库,该存的数据都放在这里了,即使爬虫是over总结:准备工作:首先,我们定义了一个电影对象,传入了URL的参数信息,设置了爬虫头,建立了数据库连接。我们通过nextpage分析每个电影页面的超链接,发现我们只是改变了参数设置创建了主进程,并编写了主进程的伪代码来启动爬虫:爬取第一页的网页内容,分析第一页内容,获取每页25个视频的详细超链接,抓取详细视频的网页内容分析第二页内容保存到每个电影对象,数据保存到数据库。思考:以上就是我们今天爬虫实战的主要内容,比较简单。第一个项目旨在让大家了解爬虫流程。同时,你还可以思考以下几点:电影详情页的短评数据、电影详情页的获奖数据、电影详情页的讨论区数据。是否可以获得以上数据?用今天的采集方式?如果不是,应该如何获取数据?再晚写的话,今天的实战项目就结束了。需要源码的同学可以在公众号后台回复豆瓣电影。正如开头所说,我们的目的不是爬取数据。因此,我将利用这些数据做一个简单的数据分析,目的很简单:了解数据分析的过程。下次见。想了想,发现写技术文章比写软文难一个档次以上。软文虽然没有什么技术含量,但是大家都爱看。技术文章苦涩难懂,但是如果像我说的那么详细,你确定不点个赞支持下?原创不易,请点赞!
