爬虫小知识:爬取网站流程
确定网站哪个url是数据的来源。简要分析一下网站结构,查看数据一般放在哪里。查看是否有分页,解决分页的问题。发送请求,查看response.text里面是否有我们想要的数据内容。如果有数据,就用相应的提取数据的方法提取数据保存。注意:1.刚开做爬虫项目,先不要用类去做,只需要关注数据的来源等问题的解决,不要关注类结构的设计。
简介: lxml 是一个HTML/XML的解析器,主要的功能是如何解析和提取 HTML/XML 数据。
lxml和正则一样,也是用 C 实现的,是一款高性能的 Python HTML/XML 解析器,我们可以利用之前学习的XPath语法,来快速的定位特定元素以及节点信息。
lxml python 官方文档: http://lxml.de/index.html
需要安装C语言库,可使用 pip 安装:pip install lxml (或通过wheel方式安装)
在这里我们用的Anaconda编译器,不需再安装
我们利用它来解析 HTML 代码
案例: 返回值-------------->element对象
etree.HTML()可以帮我们补齐标签
小结:lxml可以自动修正html代码
格式:
#初始化一个xpath解析对象 html = etree.HTML(text) result = etree.tostring(html,encoding='utf-8') ##解析对象输出代码 是一个bytes类型 print(type(html)) #<class 'lxml.etree._Element'> print(type(result)) #<class 'bytes'> print(result.decode('utf-8')) # 输出字符串 ## pretty_print=True 格式化输出,美观数据 print(etree.tostring(element对象,pretty_print=True,encoding='utf-8').decode('utf-8'))不解码是bytes类型:
案例:.decode(‘utf-8’)写在括号里面(不会加上html标签)
案例:.decode(‘utf-8’)写在括号外面(加html标签)
通过lxml模块,可以使用xpath语法来筛选元素(借用lxml模块返回的element对象)
返回值element还可以继续调用xpath方法来继续筛选元素;
element对象.xpath() 方法返回的是一个列表;
格式:
html_element.xpath() 案例:加xpath来筛选元素xpath获取标签返回的是列表,列表里面是element对象
要特别注意的是这一串代码!!!!!!!!!
result = point.xpath('//bookstore/book[position()<3]/title/text()') ## 必须指定哪个节点,用/或者不写都取不出数据,因为他不能定位到bookstore,XML并不是我们看到的这样的结构前面还有标签 代码 from lxml import etree html = ''' <bookstore> <title>新华书店</title> <book href="http://www.langlang2017.com/"> <title lang="eng">Harry Potter</title> <price>29.99</price> </book> <book> <title lang="zh">Learning XML</title> <price>39.95</price> </book> <book href="www.baidu.com"> <title>python 大全</title> <price>99.95</price> </book> </bookstore> ''' point = etree.HTML(html) # (1)获取文档中的所有book节点的第一个book节点 result = point.xpath('//book[1]') # (2)获取第一个book节点 first_book = result[0] ## 上题获取的是列表,这题获取的是element对象 print(first_book) # (3)获取first_book当前节点下的href result = first_book.xpath('./@href') ## 当前节点或者父节点下的标签用/和//效果一样,定位到bookstore下了,取得都是book,除非下面还有后代节点才有区别 # (4)获取当前节点的父节点下的book节点的href属性 result = first_book.xpath('../book/@href') # (5)获取价格小于40的书的书名 result = point.xpath('//book[price<40]/title/text()') # (6)获取价格等于99.95的书的书名 result = point.xpath('//book[price=99.95]/title/text()') # (7)获取book标签下面的title和price标签 result = point.xpath('//book/title|//book/price') # (8)获取属性href含有baidu字符串的book标签,获取此标签的书名 result = point.xpath('//book[contains(@href,"baidu")]/title/text()') # (9)获取去前面两个属于bookstore标签的字标签的book标签,获取此标签的书名 result = point.xpath('//bookstore/book[position()<3]/title/text()') ## 必须指定哪个节点,用/或者不写都取不出数据,因为他不能定位到bookstore,XML并不是我们看到的这样的结构前面还有标签 # (10)匹配任何属性节点 # result = point.xpath('//*[@*]') # (11)获取所有带有属性title标签,获取此标签的内容 # result = point.xpath('//title[@*]/text()') print(result)爬取内容
注意:分页的实现
代码
普通函数写法
import json import requests from lxml import etree ## 确定url base_url = 'https://www.shanbay.com/wordlist/110521/232414/?page=%s' ## 封装headers headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36' } def get_text(value): if value: return value[0] else: return '' words = [] ## 分页 for i in range(1,4): ## 发送请求 response = requests.get(base_url %i,headers=headers) # print(response.text) ## 如果有数据,就用相应的方法提取数据 ## 获取element对象来使用xpath方法提取数据 html = etree.HTML(response.text) tr_list = html.xpath('//tbody/tr') ## 从获取的列表中的tr获取数据 for tr in tr_list: en = get_text(tr.xpath('.//td[class="span2"]/strong/text()')) # en = tr.xpath('.//td[contains(@class,"span2")]/strong/text()') zh = get_text(tr.xpath('.//td[@class="span10"]/text()')) ### {'recipe':n. 挂起,暂停} # items() item = {} if en and zh: item[en] = zh words.append(item) with open('word.json','w',encoding='utf-8') as fp: json.dump(words,fp) 封装成面向对象 import json import requests from lxml import etree class Shanbei(object): def __init__(self,url,headers): self.words = [] self.headers = headers self.url = url self.parse() def get_text(self,value): if value: return value[0] else: return '' def parse(self): ### 分页 for i in range(1, 4): ## 发送请求 response = requests.get(self.url % i, self.headers) # print(response.text) ## 如果有数据,就用相应的方法提取数据 ## 获取element对象来使用xpath方法提取数据 html = etree.HTML(response.text) tr_list = html.xpath('//tbody/tr') ## 从获取的列表中的tr获取数据 for tr in tr_list: en = self.get_text(tr.xpath('.//td[class="span2"]/strong/text()')) # en = self.get_text(tr.xpath('.//td[contains(@class,"span2")]/strong/text()')) print(en) zh = self.get_text(tr.xpath('.//td[@class="span10"]/text()')) ### {'recipe':n. 挂起,暂停} # items() item = {} if en and zh: item[en] = zh self.words.append(item) if __name__ == '__main__': base_url = 'https://www.shanbay.com/wordlist/110521/232414/?page=%s' headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36' } sb = Shanbei(base_url,headers) with open('word.json','w',encoding='utf-8') as fp: json.dump(sb.words,fp) with open('word.json','r') as fp: list = json.load(fp) for one in list: print(one) 运行结果页面内容
注意:推荐页上面没有A,B,C等而且数据重复,热门也不符合我们对于大部分数据的处理方法,我们可以去掉
代码
普通函数写法
import json import requests from lxml import etree base_url = 'https://music.163.com/discover/artist' ### 通过url获取页面的xpath对象以便提取数据 def get_xpath(url): headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36' } response = requests.get(url,headers=headers) return etree.HTML(response.text) singer_infos = [] ## 获取简介 def parse_detail(url,item): html = get_xpath(url) desc_list = html.xpath('//div[@class="n-artdesc"]/p/text()') desc = ''.join(desc_list) item['desc'] = desc singer_infos.append(item) ### 第三步:通过传过来的A,B,Curl来提取对应页面的数据 def parse_type(url): ## 获取该页面的xpath html = get_xpath(url) singer_names = html.xpath('//ul[@id="m-artist-box"]/li/p/a/text()') singer_urls = html.xpath('//ul[@id="m-artist-box"]/li/p/a[1]/@href|//ul[@id="m-artist-box"]/li/a/@href') for i,name in enumerate(singer_names): item = {} item["歌手"] = name item['歌手连接'] = 'https://music.163.com'+singer_urls[i].strip() item['歌手详情页'] = 'https://music.163.com'+singer_urls[i].replace(r'?id','/desc?id').strip() # print(item) url = 'https://music.163.com'+singer_urls[i].replace(r'?id','/desc?id').strip() parse_detail(url,item) ### 第二步:解析每一个地区歌手页面 def parse_area(url): html = get_xpath(url) ## 获取A,B,C分类的url # type_name_url = html.xpath('//ul[@id="initial-selector"]/li[position()>1]/a/@href') type_name_url = html.xpath('//ul[@id="initial-selector"]/li[position()>1]/a/@href') # print(type_name_url) ## 找到A,B,Curl for type in type_name_url: ## 拼接url url = 'https://music.163.com'+type parse_type(url) ### 第一步:首页的解析,获取地区列表的url def parse(): html = get_xpath(base_url) # print(html) area_singer_urls = html.xpath('//ul[@class="nav f-cb"]/li/a/@href') #################### 去掉热门和推荐url############### area_singer_urls = [x for x in area_singer_urls if 'id' in x] ################ 拿到剩下的url ################# for url in area_singer_urls: new_url = 'https://music.163.com'+url parse_area(new_url) if __name__ == '__main__': parse() with open('singer.json','w',encoding='utf-8') as fp: json.dump(singer_infos,fp) with open('singer.json','r') as fp: print(json.load(fp)) 封装成面向对象 import json import requests from lxml import etree class Music_163: def __init__(self,url): self.url = url self.singer_infos = [] ### 通过url获取页面的xpath对象以便提取数据 def get_xpath(self,url): headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36' } response = requests.get(url, headers=headers) return etree.HTML(response.text) ## 获取简介 def parse_detail(self,url, item): html = self.get_xpath(url) desc_list = html.xpath('//div[@class="n-artdesc"]/p/text()') desc = ''.join(desc_list) print(desc) item['desc'] = desc self.singer_infos.append(item) ### 第三步:通过传过来的A,B,Curl来提取对应页面的数据 def parse_type(self,url): ## 获取该页面的xpath html = self.get_xpath(url) singer_names = html.xpath('//ul[@id="m-artist-box"]/li/p/a/text()') singer_urls = html.xpath('//ul[@id="m-artist-box"]/li/p/a[1]/@href|//ul[@id="m-artist-box"]/li/a/@href') for i, name in enumerate(singer_names): item = {} item["歌手"] = name item['歌手连接'] = 'https://music.163.com' + singer_urls[i].strip() item['歌手详情页'] = 'https://music.163.com' + singer_urls[i].replace(r'?id', '/desc?id').strip() # print(item) url = 'https://music.163.com' + singer_urls[i].replace(r'?id', '/desc?id').strip() self.parse_detail(url, item) ### 第二步:解析每一个地区歌手页面 def parse_area(self,url): html = self.get_xpath(url) ## 获取A,B,C分类的url type_name_url = html.xpath('//ul[@id="initial-selector"]/li[position()>1]/a/@href') # print(type_name_url) ## 找到A,B,Curl for type in type_name_url: ## 拼接url url = 'https://music.163.com' + type self.parse_type(url) ### 第一步:首页的解析,获取地区列表的url def parse(self): html = self.get_xpath(self.url) # print(html) area_singer_urls = html.xpath('//ul[@class="nav f-cb"]/li/a/@href') #################### 去掉热门和推荐url############### area_singer_urls = [x for x in area_singer_urls if 'id' in x] ################ 拿到剩下的url ################# for url in area_singer_urls: new_url = 'https://music.163.com' + url self.parse_area(new_url) if __name__ == '__main__': base_url = 'https://music.163.com/discover/artist' m = Music_163(base_url) m.parse() with open('singer.json', 'w', encoding='utf-8') as fp: json.dump(m.singer_infos, fp) with open('singer.json','r') as fp: print(json.load(fp)) 运行结果