beautifulsoup:是一款非常强大的工具,爬虫利器。“美味的汤,绿色的浓汤”。一个灵活又方便的网页解析库,处理高效,支持多种解析器。 利用它就不用编写正则表达式也能方便的实现网页信息的抓取。
lxml:是python的一个解析库,支持HTML和XML的解析,支持XPath解析方式,而且解析效率非常高。(具体可参考:https://www.cnblogs.com/zhangxinqi/p/9210211.html ) (XPath:全称XML Path Language,即XML路径语言,是一门在XML文档中查找某部分位置信息的语言,最初是用来搜寻XML文档节点信息的,但是它同样适用于HTML文档的搜索。可参考:https://zhuanlan.zhihu.com/p/37177627 XPath的选择功能十分强大,它提供了非常简明的路径选择表达式,另外,它还提供了超过100个内建函数,用于字符串、数值、时间的匹配以及节点、序列的处理等,几乎所有我们想要定位的节点,都可以用XPath来选择。 XPath于1999年11月16日成为W3C标准,它被设计为供XSLT、XPointer以及其他XML解析软件使用,更多的文档可以访问其官方网站:https://www.w3.org/TR/xpath/ )
本次demo目的:
准备工作:安装bs4库,因用到解析库lxml,so也要pip install下lxml模块。(sublime的python环境配置这里就不多做赘述)
参考用例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/> <title>测试bs4</title> <meta name="description" content=""/> <meta name="keywords" content=""/> <link href="" rel="stylesheet"/> </head> <body> <div> <p>python</p> </div> <div class="song" > 唐宋八大家 <a href="http://www.song.com/" title="豪放派" target="_self"> <p class='su'> <a href="" id='su'>苏轼</a></p> <p>...</p> <p>唐有韩柳,宋为欧阳、三苏和曾王</p> </a> <a href="" class="du" title="婉约派">李清照</a> <p id='li'> <a href="" id='li'>千古第一才女李清照</a></p> <img src="http://www.baidu.com/meinv.jpg" alt=""> </div> <div class="tang"> <ul> <li><b>生当作人杰,死亦为鬼雄</b></li> <li><i>易安居士</i></li> <li><a href="http://www.baidu.com" title="qing">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村</a></li> <li><a href="http://www.163.com" title="qin">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山</a></li> <li><a href="http://www.126.com" alt="qi">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君</a></li> <li><a href="http://www.haha.com" id='feng'>凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘</a></li> <li><a href="http://www.sina.com" class="du">杜甫</a></li> <li><a href="http://www.dudu.com" class="du">杜牧</a></li> </ul> </div> </body> </html>
from bs4 import BeautifulSoup # 1.生成对象 soup = BeautifulSoup(open('soup_text.html', encoding='utf-8'), 'lxml') print(soup.prettify()) print(type(soup)) print(soup) # 标签选择器:通过这种soup.标签名查找,如果文档中有多个这样的标签,只能找到第一个符合要求的标签 print(soup.title.name) # 获取名称 print(type(soup.title)) print(soup.head) print(soup.p) print(soup.div) # 2.获取属性 # soup.a.arrts # 获取a标签所有属性和值,返回一个字典 # soup.a.attrs['href'] # 获取href属性 # soup.a['href'] # 也可简写成这种 #上面两种方式都可以获取a标签的href属性值 print(soup.a['href']) print(soup.a['title']) print(soup.a['target']) print(soup.a.attrs['href']) #attrs可以传入字典的方式来查找标签,但是这里有个特殊的就是class,因为class在python中是特殊的字段,所以如果想要查找class相关的可以更改attrs={'class_':'element'}或者soup.find_all('',{"class":"element}),特殊的标签属性可以不写attrs,例如id print(soup.find_all(attrs={'id': 'li'})) # 3.获取内容 # soup.a.text # soup.a.get_text() # soup.a.string # 只能够获取文本,如果标签里还有标签就返回None,而前两个,可以获取所有文本内容 print(soup.a.get_text()) print(soup.div.text) print(soup.div.string) print(soup.div.get_text()) #嵌套选择 #我们直接可以通过下面嵌套的方式获取子节点和子孙节点 print(soup.head.title.string) # 4.find # soup.find('a') #找到第一个符合要求的标签 # print(soup.find('a', title='qin')) # print(soup.find('a', alt='qi')) # print(soup.find('a', class_='du')) # print(soup.find('a', id='feng')) # print(soup.find('a', class_='du')) # 注意使用class_,区别于类关键字 # div = soup.find('div', class_='tang') # print(div) # print(div.find('a', alt='qi')) print(div.find('a', class_='du')) # find方法不仅soup可以调用,普通的div对象也能调用,会去指定的div里面去查找符合要求的节点 # 5.find_all lt = soup.find_all('a') print('soup.find_all\'s type:', type(lt)) div = soup.find('div', class_='tang') a = div.find_all('a') # print(a) print('soup.find().find_all\'s type:', type(a)) # <class 'bs4.element.ResultSet'> print('soup.find().find_all element\'s type:', type(a[0])) # <class 'bs4.element.ResultSet'> # print(a[0].get_text('href')) print(div.find_all(['a', 'b'])) # 返回列表内所有的标签内容 # print(div.find_all('a', limit=2)) # 取前两个 # find、find_all返回列表,类型都为<class 'bs4.element.ResultSet'>,元素为<class 'bs4.element.Tag'> # find_all(name,attrs,recursive,text,**kwargs) # find(name,attrs,recursive,text,**kwargs) # 可以根据标签名,属性,内容查找文档。 # find返回的匹配结果的第一个元素其他一些类似的用法: # find_parents()返回所有祖先节点,find_parent()返回直接父节点。 # find_next_siblings()返回后面所有兄弟节点,find_next_sibling()返回后面第一个兄弟节点。 # # find_previous_siblings()返回前面所有兄弟节点,find_previous_sibling()返回前面第一个兄弟节点。 # find_all_next()返回节点后所有符合条件的节点, find_next()返回第一个符合条件的节点 # find_all_previous()返回节点后所有符合条件的节点, find_previous()返回第一个符合条件的节点 # 6.select s = soup.select('.song') print(len(s)) print('soup.select\'s type:', type(s)) # 返回列表<class 'list'> # print(s[0]) print('select\'s element type:', type(s[0])) # <class 'bs4.element.Tag'> s1 = soup.select('.song a') # 组合查找,可层级筛选 print(s1) print(s1[0].get('href')) s2 = soup.select('.song img') print(s2[0].get('src')) # print(s2[0].get('alt')) print(soup.select(' p #su')) # 组合查找,p标签下id为su的内容 print(soup.select(' p.su')) # 组合查找,class为su的p标签内容 print(soup.select('p#li')) # 组合查找,id为li的p标签的内容 print(soup.select('head>title')) # 直接子标签查找 # select返回列表,类型为<class 'list'>,list中元素为<class 'bs4.element.Tag'>
具体使用:
from bs4 import BeautifulSoup #设置头部信息 ... headers={ 'cookie': cookie, # 自行补充cookies 'referer': referer, # 跳转自的页面 'user-agent': user_agent # 伪装浏览器引擎 } main_count = 1
# 获取具体图片所在url,并下载相应图片信息 def get_url(url, headers): global main_count print('******主页第{}页,url:{}*****'.format(main_count, url)) res = requests.get(url, headers=headers) soup = BeautifulSoup(res.text, 'lxml') div = soup.find('div', class_='main-content') # 限定div,尽量缩小查找节点的范围,避免获取到垃圾url造成不必要麻烦 for i in div.find_all(target='_blank'): if i.select('img'): img_url = i.get('href') print(img_url) # 获取图片 get_second_parse(img_url, headers) time.sleep(2) # 由于网站图片集有分页,所以需要处理分页,获取下一图集所在url,并回调 if soup.select('a.next.page-numbers')[0].get('href'): next_url = soup.select('a.next.page-numbers')[0].get('href') main_count += 1 get_url(next_url, headers) # 获取具体图片所在url,并下载相应图片信息 def get_url(url, headers, img_num_per_page): global main_count print('******主页第{}页,url:{}*****'.format(main_count, url)) res = requests.get(url, headers=headers) soup = BeautifulSoup(res.text, 'lxml') div = soup.find('div', class_='main-content') # 限定div,尽量缩小查找节点的范围,避免获取到垃圾url造成不必要麻烦 for i in div.find_all(target='_blank'): if i.select('img'): img_url = i.get('href') print(img_url) # 获取图片 get_inside_parse(img_url, headers, img_num_per_page) # break time.sleep(2) # 由于网站图片集有分页,所以需要处理分页,获取下一图集所在url,并回调 if soup.select('a.next.page-numbers')[0].get('href'): next_url = soup.select('a.next.page-numbers')[0].get('href') main_count += 1 get_url(next_url, headers, img_num_per_page) # 下载每张照片,注意单位时间访问服务器的频率防止被forbidden def get_inside_parse(lst_url, headers, img_num_per_page): count = 1 print('second_parse') res = requests.get(lst_url, headers=headers) time.sleep(1) try: img_title_soup = BeautifulSoup(res.text, 'lxml') if img_title_soup.select('.main-title')[0].get_text(): img_title = img_title_soup.select('.main-title')[0].get_text() # print(img_title) for index in range(1, img_num_per_page): # 最多下载到img_num_per_page-1张,可自行设置 page_url = lst_url + '/' + str(index) res = requests.get(page_url, headers=headers) time.sleep(1) print(page_url, res.status_code) if res.status_code != 200: # 超出图片列表,status错误则跳出 break soup = BeautifulSoup(res.text, 'lxml') # 注意select的使用,select返回值类型:<class 'bs4.element.ResultSet'>列表 if soup.select('.main-image p a img')[0].get('src'): img_url = soup.select('.main-image p a img')[0].get('src') filename = 'my_imgs/{}'.format(img_title) if not os.path.exists(filename): os.makedirs(filename) print('正在下载{}第{}张图片'.format(img_title, count)) print(soup.select('.main-image p a img')[0].get('src')) print(filename) dirname = '%s/%s.jpg' % (filename, count) with open(dirname, 'wb+') as jpg: jpg.write(requests.get(img_url, headers=headers).content) time.sleep(1) count += 1 else: print('URL:{}无main-title').format(lst_url) except ValueError as e: print(e.ValueError) if __name__ == '__main__': get_url(url, headers, 21)
过程问题:
-
find、find_all返回列表,类型都为<class 'bs4.element.ResultSet'>,元素为<class 'bs4.element.Tag'>
-