使用爬虫爬取页面步骤:
- step1:使用urllib库或者request库得到页面,一般用request,比较方便
- step2:我们request得到的对象进行解码处理,因为在网页和我们电脑上进行传输和存储的数据一般为bytes类型,而在pytho中对其操作则需要字符串类型
- step3:在我们要爬取的网页端检查元素,分析网页结构
- step4:使用xpath或者bs4或者正则表达式对页面进行解析(这里以xpath举例,因为后面的我还没学^_^)
- step5:将解析的结果保存就ok了
豆瓣电影
如果我们需要获得即将上市的电影的信息:
首先使用request库请求页面,然后对得到的响应进行解码处理:
headers = { 'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36', 'Referer' : 'https://movie.douban.com/' #如果有的话,一定要加上,反爬虫机制 } url = 'https://movie.douban.com/cinema/nowplaying/wuhan/' response = requests.get(url, headers = headers) response = response.text #类型要变成text(string类型),或者content.decode('你需要的编码格式'),否则etree.HTML会出错
然后分析页面,可以发现,我们所需要的信息,都放在一个无序列表中,无序列表有一个属性:class = 'lists',我们先根据这个来定位。
result = etree.HTML(response) #使用etree下的HTML方法构造一个htmlElement类型的元素 uls = result.xpath("//ul[@class = 'lists']")[0] #获取第一个ul中class属性值为lists的对象 lis = uls.xpath("./li") #获取其下的所有li标签,因为我们所需要的信息都保存在li标签中
现在,即将上映的电影的信息,都被保存在了lis这个列表中,我们遍历列表,然后以字典的形式存储信息
在属性中,比较特殊的是海报的封面,它并不是直接出现在li标签中,而是其下面的标签,如图:
for li in lis: title = li.xpath("@data-title")[0] #获取li标签中,元素为data-title的西信息 duration = li.xpath("@data-duration")[0] region = li.xpath("@data-region")[0] director = li.xpath("@data-director")[0] actors = li.xpath("@data-actors")[0] pic = li.xpath(".//img/@src")[0] #由于图片比较特殊,所以与前面的不太一样 movie = { "title" : title, "duration" : duration, "region" : region, "director" : director, "actors" : actors, "pic" : pic } movies.append(movie) #保存到字典中 for movie in movies : print(movie)
这就是爬取豆瓣电影的全部过程了,相对来说是非常简单的
全部代码:
import requests from lxml import etree headers = { 'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36', 'Referer' : 'https://movie.douban.com/' #如果有的话,一定要加上,反爬虫机制 } url = 'https://movie.douban.com/cinema/nowplaying/wuhan/' response = requests.get(url, headers = headers) response = response.text #类型要变成text(string类型),否则etree.HTML会出错 result = etree.HTML(response) #使用etree下的HTML方法构造一个htmlElement类型的元素 uls = result.xpath("//ul[@class = 'lists']")[0] #获取第一个ul中class属性值为lists的对象 lis = uls.xpath("./li") #获取其下的所有li标签 movies = [] for li in lis: title = li.xpath("@data-title")[0] #分别获取对象 duration = li.xpath("@data-duration")[0] region = li.xpath("@data-region")[0] director = li.xpath("@data-director")[0] actors = li.xpath("@data-actors")[0] pic = li.xpath(".//img/@src")[0] movie = { "title" : title, "duration" : duration, "region" : region, "director" : director, "actors" : actors, "pic" : pic } movies.append(movie) #保存到字典中 for movie in movies : print(movie)
执行结果:
电影天堂
如果我们要获取电影天堂前七页中的电影的详细信息
红框中表示我们需要获取的信息:
url获取
首先考虑获取每一页电影的url,多点几个页面,发现有如下规律,当我们在第二页时,域名显示如下:
在第三页时,域名为:https://www.dytt8.net/html/gndy/dyzz/list_23_3.html
在第四页时,域名为:https://www.dytt8.net/html/gndy/dyzz/list_23_4.html
...
再从其他页面跳转到第一页时,域名为https://www.dytt8.net/html/gndy/dyzz/list_23_1.html
那么规律就很明显了,所以我们第一步应该是先要获取到每一中,电影的url,检查页面可知,a标签被放在一个table标签中,class为tbspan,所以可以根据这个来选择。
for index in range(1, 8): url = 'https://www.dytt8.net/html/gndy/dyzz/list_23_' + str(index) +'.html' #页面的url detail_urls = get_detail_urls(url) #获取当前页面中,所有电影的url def get_detail_urls(url): response = requests.get(url, headers = headers, verify = False) text = response.content.decode('gbk', 'ignore') #由于电影天堂这个网站并不是很规范,用response.text的时候,会出现乱码,所以这里用content然后自己解码 # 构造htmlElement对象 html = etree.HTML(text) detail_urls = html.xpath("//table[@class = 'tbspan']//a/@href") #获取url #加上主域名 for index in range(len(detail_urls)): #看上图可知,获取到的url都只有后半截,所以我们需要手动加上前半截 BASE = 'https://www.dytt8.net' detail_urls[index] = BASE + detail_urls[index] return detail_urls
详情页获取
url获取完成之后,我们就需要根据这些url获取每一个电影详情页里面的东西了,包括电影名、海报封面、译名、片名、产地、简介、下载地址这些东西。
def start(): movies = [] for index in range(1, 8): url = 'https://www.dytt8.net/html/gndy/dyzz/list_23_' + str(index) +'.html' detail_urls = get_detail_urls(url) #当前页面的所有url拿到手 for detail_url in detail_urls: #对于每个url,也就是每一部电影 movie = parse_detail_page(detail_url) #解析详情页面,并返回解析的结果,这个函数后面会实现 movies.append(movie) #将电影信息保存在列表中 print(movie)
详情页解析:
首先看看详情页的信息,可以发现是非常的.......真的一点都不规范,所有的信息都被包裹在一个p标签中,所以获取信息非常麻烦
首先获取标题,根据class和font color来获取:
def parse_detail_page(url): movie = {} requests.packages.urllib3.disable_warnings() response = requests.get(url, headers=headers, verify=False) text = response.content.decode('gbk', 'ignore') html = etree.HTML(text) title = html.xpath("//div[@class = 'title_all']//font[@color = '#07519a']/text()")[0] #获取标题 movie['title'] = title return movie
然后获取图片,根据Zoom和img来获取,图片这里要说一下,因为可能有的页面有两张图片:一个是海报封面post,另一个是电影截图screenshot,也有的页面一张post,没有screenshot,也有的都没有,所以我们获取了图片信息之后要先判断一下
ZoomElement = html.xpath("//div[@id='Zoom']")[0] photo = ZoomElement.xpath(".//img/@src") if (len(photo) > 0): #如果photo这个列表中包含了图片,那就说明有海报: movie['cover'] = photo[0] else: movie['cover'] = "" if (len(photo) > 1): #如果photo包含了两张或两张以上的图片,则说明有截图 movie['screenshot'] = photo[1] else: movie['screenshot'] = ""
然后获取下方的文字信息:
infos = ZoomElement.xpath(".//text()") #看下方的注解1 def parse_info(info, rule): return info.replace(rule, "").strip() for index, info in enumerate(infos): #index为下标,info为内容 if info.startswith("◎年 代"): #如果是以年代开头 info = parse_info(info, "◎年 代") #那么将字符串中“◎年 代”这个字符使用replace函数替换为空格,然后使用strip函数去除前后空格 movie['year'] = info #最终得到的就是一个年份数字,没有乱七八糟的空格字符啥的 elif info.startswith("◎产 地"): info = parse_info(info, "◎产 地") movie['place'] = info elif info.startswith("◎类 别"): info = parse_info(info, "◎类 别") movie['category'] = info elif info.startswith("◎豆瓣评分"): info = parse_info(info, "◎豆瓣评分") movie['douban_rating'] = info elif info.startswith("◎片 长"): info = parse_info(info, "◎片 长") movie['duration'] = info elif info.startswith("◎导 演"): info = parse_info(info, "◎导 演") elif info.startswith("◎主 演"): #到主演这里需要注意,因为可能一部电影中有很多主演,看注解2 info = parse_info(info, "◎主 演") #首先处理第一行的主演,将其加入actor列表中 actors = [info] for x in range(index + 1, len(infos)): #然后往后循环,只要不到◎这个符号,那就说明是主演,继续提取 actor = infos[x].strip() if (actor.startswith("◎")): break actors.append(actor) movie['actors'] = actors elif info.startswith("◎简 介"): info = parse_info(info, "◎◎简 介") profile = [info] for x in range(index + 1, len(infos)): profile = infos[x].strip() movie['profile'] = profile download_url = html.xpath("//td[@bgcolor = '#fdfddf']/a/@href") #获取download链接 if len(download_url) > 0: #因为有的页面可能没有下载链接,所以判断一下 movie['download_url'] = download_url[0] else: movie['downloada_url'] = ""
注解1:使用ZoomElement下,使用text得到问题,会返回一个列表,列表中的一个元素,就是下图中的一行:
注解2:可能一部电影中有很多个主演,我们需要将所有的主演都保存下来
全部的代码
import requests from lxml import etree BASE = 'https://www.dytt8.net' headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36' } def get_detail_urls(url): response = requests.get(url, headers=headers, verify=False) text = response.content.decode('gbk', 'ignore') # 构造htmlElement对象 html = etree.HTML(text) detail_urls = html.xpath("//table[@class = 'tbspan']//a/@href") # 加上主域名 for index in range(len(detail_urls)): detail_urls[index] = BASE + detail_urls[index] return detail_urls def parse_detail_page(url): movie = {} response = requests.get(url, headers=headers, verify=False) text = response.content.decode('gbk', 'ignore') html = etree.HTML(text) title = html.xpath("//div[@class = 'title_all']//font[@color = '#07519a']/text()")[0] movie['title'] = title ZoomElement = html.xpath("//div[@id='Zoom']")[0] photo = ZoomElement.xpath(".//img/@src") if len(photo) > 0: movie['cover'] = photo[0] else: movie['cover'] = "" if len(photo) > 1: movie['screenshot'] = photo[1] else: movie['screenshot'] = "" infos = ZoomElement.xpath(".//text()") def parse_info(info, rule): return info.replace(rule, "").strip() for index, info in enumerate(infos): #index为下标,info为内容 if info.startswith("◎年 代"): #如果是以年代开头 info = parse_info(info, "◎年 代") #那么将字符串中“◎年 代”这个字符使用replace函数替换为空格,然后使用strip函数去除前后空格 movie['year'] = info #最终得到的就是一个年份数字,没有乱七八糟的空格字符啥的 elif info.startswith("◎产 地"): info = parse_info(info, "◎产 地") movie['place'] = info elif info.startswith("◎类 别"): info = parse_info(info, "◎类 别") movie['category'] = info elif info.startswith("◎豆瓣评分"): info = parse_info(info, "◎豆瓣评分") movie['douban_rating'] = info elif info.startswith("◎片 长"): info = parse_info(info, "◎片 长") movie['duration'] = info elif info.startswith("◎导 演"): info = parse_info(info, "◎导 演") elif info.startswith("◎主 演"): info = parse_info(info, "◎主 演") actors = [info] for x in range(index + 1, len(infos)): actor = infos[x].strip() if (actor.startswith("◎")): break actors.append(actor) movie['actors'] = actors elif info.startswith("◎简 介"): info = parse_info(info, "◎简 介") profile = [info] for x in range(index + 1, len(infos)): profile = infos[x].strip() if profile.startswith('【下载地址】') : break movie['profile'] = profile download_url = html.xpath("//td[@bgcolor = '#fdfddf']/a/@href") if len(download_url) > 0: movie['download_url'] = download_url[0] else: movie['downloada_url'] = "" return movie def start(): movies = [] for index in range(1, 8): url = 'https://www.dytt8.net/html/gndy/dyzz/list_23_' + str(index) + '.html' detail_urls = get_detail_urls(url) for detail_url in detail_urls: movie = parse_detail_page(detail_url) movies.append(movie) print(movie) if __name__ == '__main__': start()
运行结果:
由于request版本的原因,所以有些版本可能会报ssl证书错误(我在pycharm上可以跑,但是jupyter上面就不行),这个我找了好久也没找到解决方案,如果有大佬了解的话,可以告诉我一下^_^
Comments NOTHING