环境python3.9版本及以上,开发工具pycharm
XPath解析:
XPath是一门在XML文档中查找信息的语言,XPath可以用来在XML文档中对元素和属性进行遍历,而我们熟知的HTML恰巧属于XML中的一个子集,所以完全可以用XPath去查找html中的内容。
首先看:
<book> <id>1</id> <name>野花遍地香</name> <price>1.23</price> <author> <nick id="10086">周大强</nick> <nick id="10010">周芷诺</nick> </author> </book>
在上述html中,book、id、name、price……都被称为节点;id、name、price、author都被称为book的子节点;book被称为id、name、price、author的父节点;id、name、price、author被称为兄弟节点。
下面通过一些例子来理解:
from lxml import etree # 如果pycharm报错,可以考虑这种导入方式 # from lxml import html # etree = html,etree xml = ''' <book> <id>1</id> <name>野花遍地香</name> <price>1.23</price> <nick>臭豆腐</nick> <author> <nick id="10086">周大强</nick> <nick id="10010">周芷诺</nick> <nick class="jay">周杰伦</nick> <nick class="jolin">蔡依林</nick> <div> <nick>惹了</nick> </div> </author> <partner> <nick id="ppc">胖胖陈</nick> <nick id="ppbc">胖胖不沉</nick> </partner> </book> ''' # 此时练习只使用XML et = etree.XML(xml) # result = et.xpath('/book') # / 表示根节点 # result = et.xpath('/book/name') # 在xpath中间的/表示的是儿子 # result = et.xpath('/book/name/text()')[0] # text() 拿文本 # result = et.xpath('/book//nick') # // 表示的是子孙后代 # result = et.xpath('/book/*/nick/text()') # * 通配符 # result = et.xpath('/book/author/nick[@class="jay"]/text()') # []表示属性筛选,@属性名=值 类似bs4中find(Nick,attrs={'class': 'jay'}) result = et.xpath('/book/partner/nick/@id') # 最后一个/表示拿到Nick里面的id的内容,@属性。可以直接拿到属性值 print(result) # xpath处理HTML html = ''' <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Title</title> </head> <body> <ul> <li><a href="http://www.baidu.com">百度</a></li> <li><a href="http://www.google.com">谷歌</a></li> <li><a href="http://www.sogou.com">搜狗</a></li> </ul> <ol> <li><a href="feiji">飞机</a></li> <li><a href="dapao">大炮</a></li> <li><a href="huoche">火车</a></li> </ol> <div class="job">李嘉诚</div> <div class="common">胡辣汤</div> </body> </html> ''' et = etree.HTML(html) # li_list = et.xpath('/html/body/ul/li[2]/a/text()') # print(li_list) ll = et.xpath('//li') for i in ll: href = i.xpath('./a/@href')[0] # ./表示当前节点 text = i.xpath('./a/text()')[0] # ./表示当前节点 print(text, href)
下面是对基础知识掌握后,跟做的一个案例:
''' 1.拿到页面源代码 2.从页面源代码中提取你需要的数据,价格,名称, 公司名称 ''' import requests from lxml import etree url = "https://beijing.zbj.com/search/service/?l=0&kw=saas&r=2" resp = requests.get(url) resp.encoding = 'utf-8' # print(resp.text) # 提取数据 et = etree.HTML(resp.text) divs = et.xpath('//div[@class="service-card-wrap"]') for div in divs: # 此时的div就是一条数据,对应一个商品信息 # 商品价格 price = div.xpath("./div/div[3]/div[1]/span/text()") if not price: # 过滤无用数据 continue price = price[0] company = div.xpath("./div/a/div[2]/div[1]/div/text()")[0] name = div.xpath("./div/div[3]/div[2]/a/text()")[0] name = ''.join(name) print(name+"\t", company+"\t", price+"\t")
由于现在这个界面变成了JS动态界面,我们发送一次请求只能获取到10条数据,然而在我们查看页面源代码时,发现它的HTML框架存在,数据也存在,但是是一次请求时分成多个时延发送,所以我们请求一次值获取到了第一次出现的数据,那些后面缓冲出来的数据并没有一次性爬取到,当我们把过滤无用数据这段代码去掉,就可以发现剩下的40条数据都为空,所以此次数据爬虫应当算成功!
PyQuery解析:
直接看代码
from pyquery import PyQuery # html = """ # <ul> # <li class="aaa"><a href="http://www.google.com">谷歌</a></li> # <li class="aaa"><a href="http://www.baidu.com">百度</a></li> # <li class="bbb" id='qq'><a href="http://www.qq.com">腾讯</a></li> # <li class="bbb"><a href="http://www.yuanlai.com">源来</a></li> # </ul> # """ # 加载html内容 # p = PyQuery(html) # print(p) # print(type(p)) # pyquery对象接(css选择器) # a = p('a') # print(a) # print(type(a)) # 依然是pyquery对象 # 链式操作 # a = p("li")("a") # print(a) # a = p("li a") # print(a) # a = p(".aaa a") # class="aaa" # print(a) # a = p("#qq a") #id="qq" # print(a) # href = p("#qq a").attr("href") # 拿属性 # text = p("#qq a").text() # 拿文本 # print(text) # 坑,如果多个标签同时拿属性,只能默认拿到第一个 # href = p("li a").attr("href") # print(href) # 多个标签拿属性 # it = p("li a").items() # for item in it: # 从迭代器中拿到每一个标签 # href = item.attr("href") # 拿到href属性 # text = item.text() # print(text, href) # 快速总结: # 1.pyquery(选择器) # 2.item() 当选择器选择的内容有很多时,需要一个一个处理时‘ # 3.attr(属性名):获取属性信息 # 4.text() 获取文本 # div = ''' # <div><span>i love you</span></div> # ''' # p = PyQuery(div) # html = p("div").html() # 全都要 # text = p("div").text() # 只要文本,所有的html标签都被过滤掉 # print(html) # print(text) html = ''' <HTML> <div class='aaa'>哒哒哒</div> <dic class='bbb'>嘟嘟嘟</div> </HTML> ''' p = PyQuery(html) # p("div.aaa").after('''<div class='ccc'>吼吼吼</div>''') # 在xxxx标签后面添加xxxx新标签 # p("div.aaa").append('''<span>i love you</span>''') # 在xxxx标签中添加添加xxxx新标签 # # p("div.bbb").attr(("class", "aaa")) # 修改属性 # p("div.bbb").attr("id", "123456") #新增属性,前提是该标签,没有这个属性 # p("div.bbb").remove_attr("id") # 删除属性 # p("div.bbb").remove() # 删除标签 # print(p) from pyquery import PyQuery # html = """ # <ul> # <li class="aaa"><a href="http://www.google.com">谷歌</a></li> # <li class="aaa"><a href="http://www.baidu.com">百度</a></li> # <li class="bbb" id='qq'><a href="http://www.qq.com">腾讯</a></li> # <li class="bbb"><a href="http://www.yuanlai.com">源来</a></li> # </ul> # """ # 加载html内容 # p = PyQuery(html) # print(p) # print(type(p)) # pyquery对象接(css选择器) # a = p('a') # print(a) # print(type(a)) # 依然是pyquery对象 # 链式操作 # a = p("li")("a") # print(a) # a = p("li a") # print(a) # a = p(".aaa a") # class="aaa" # print(a) # a = p("#qq a") #id="qq" # print(a) # href = p("#qq a").attr("href") # 拿属性 # text = p("#qq a").text() # 拿文本 # print(text) # 坑,如果多个标签同时拿属性,只能默认拿到第一个 # href = p("li a").attr("href") # print(href) # 多个标签拿属性 # it = p("li a").items() # for item in it: # 从迭代器中拿到每一个标签 # href = item.attr("href") # 拿到href属性 # text = item.text() # print(text, href) # 快速总结: # 1.pyquery(选择器) # 2.item() 当选择器选择的内容有很多时,需要一个一个处理时‘ # 3.attr(属性名):获取属性信息 # 4.text() 获取文本 # div = ''' # <div><span>i love you</span></div> # ''' # p = PyQuery(div) # html = p("div").html() # 全都要 # text = p("div").text() # 只要文本,所有的html标签都被过滤掉 # print(html) # print(text) html = ''' <HTML> <div class='aaa'>哒哒哒</div> <dic class='bbb'>嘟嘟嘟</div> </HTML> ''' p = PyQuery(html) # p("div.aaa").after('''<div class='ccc'>吼吼吼</div>''') # 在xxxx标签后面添加xxxx新标签 # p("div.aaa").append('''<span>i love you</span>''') # 在xxxx标签中添加添加xxxx新标签 # # p("div.bbb").attr(("class", "aaa")) # 修改属性 # p("div.bbb").attr("id", "123456") #新增属性,前提是该标签,没有这个属性 # p("div.bbb").remove_attr("id") # 删除属性 # p("div.bbb").remove() # 删除标签 # print(p)
下面是跟做未成功的案例(它网站变化了,我试过自己的方式,没成功,所以就把老师的抄下来以便自己学习)
''' 1.提取页面源代码 2.解析页面源代码,提取数据 ''' import requests from pyquery import PyQuery def get_page_source(url): resp = requests.get(url) # resp.encoding = 'gbk' return resp.text def parse_page_source(html): doc = PyQuery(html) mt_list = doc(".my-10").items() # class="mt-10" for mt in mt_list: # 拿到每一个mt # 判断是否有汽车经销商 if not mt("div > dl:nth-child(3) > dt:contains(购车经销商)"): mt("div > dl:nth-child(2)").after(PyQuery(''' <dl class="choose-dl"> <dt>购车经销商</dt> <dd> <a href="###" class="js-dearname" data-val="125965,47759"   </a> </dd> </dl> ''')) # 向 地点 后添加购车经销商进去 # 提取购买的车型 # 想要在已经提取的内容中获取第一个怎么办? eq(0) # nth—child(1) 在css进行选择的时候,选取第一个位置的内容 chexing = mt("div > dl:nth-child(1) > dd").eq(0).text().replace("\n", "").replace(" ", "") didian = mt("div > dl:nth-child(2) > dd").text() shijian = mt("div > dl:nth-child(4) > dd").text() jiage = mt("div > dl:nth-child(5) > dd").text().replace(" 万元", "") youhao = mt("div > dl:nth-child(6) > dd > p:nth-child(1)").text().replace(" 升/百公里", "") gonglishu = mt("div > dl:nth-child(6) > dd > p:nth-child(2)").text().replace(" 公里", "") other = mt("div > div > dl > dd").text().split() # 存储到文件中 with open("A8.csv", mode="w", encoding='utf-8') as f: f.write(chexing,didian, shijian, jiage, youhao, gonglishu, other) f.close() def main(): url = 'https://k.autohome.com.cn/146/' # 1.提取页面源代码 html = get_page_source(url) # 2.解析页面源代码,提取数据 parse_page_source(html) if __name__ == "__main__": main()