新手--某美女图片爬取
代码写的很糙,希望各位大佬莫笑,自学了一段时间,还在学习的过程中昨天学了bs4,就想哪来练练手,于是有了今天的这段代码,希望大佬指正一下,代码不通顺需要提高的地方,并注释一下,将不胜感激!!!
代码如下:
```
# coding:utf-8 学好python,天天向上
import requests
from bs4 import BeautifulSoup
import time
ms=int(input('请输入爬取内容:1、性感美女 2、清纯可爱 3、性感御姐 4、制服诱惑'))
page=1
url1="https://dimgw.us/xinggan/page/"+str(page) # 性感美女
url2="https://dimgw.us/qc/page/"+str(page) # 清纯可爱
url3="https://dimgw.us/yj/page/"+str(page) # 性感御姐
url4="https://dimgw.us/zf/page/"+str(page) # 制服诱惑
if ms==1:
url=url1
elif ms==2:
url=url2
elif ms==3:
url=url3
elif ms==4:
url=url4
else:
print('输入有误,请重新输入')
headers={
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36",
"Referer":"https://dimgw.us/zf"
}
domain_resp=requests.get(url,headers=headers)
domain_page=BeautifulSoup(domain_resp.text,'html.parser')
# print(domain_page)
a_tag=domain_page.find_all('h2',class_="entry-title") # 拿到想要的标签内容
# print(a_tag)
while True:
for child_href in a_tag:
b_tag=child_href.find_all('a')
# print(b_tag)
c_tag=str(b_tag) # 对内容进行切割,获取网址
# href=c_tag.get('href')
# print(c_tag)
child_resp=requests.get(c_tag,headers=headers)
child_page=BeautifulSoup(child_resp.text,'html.parser')
# print(child_page)
# 未完成,子页面内图片跳转到另外网站
div=child_page.find('div',class_="entry-content u-text-format u-clearfix").find_all('img')# 获取子页面内img标签内容
# print(div)
for it in div:
src=it.get('src') # 拿到页面内的图片下载地址
img_name= src.split('/')[-1]
# print(src)
img=requests.get(src,headers=headers)
img.content
with open(r'练习小说/'+img_name,'wb') as f: # 报错:OSError: Invalid argument:
f.write(img.content)
print('完成',img_name)
time.sleep(1)
f.close()
page+=1
print('下载完成')
``` - 我也在学爬虫,来讨论一下
# 1
```py
ms=int(input('请输入爬取内容:1、性感美女 2、清纯可爱 3、性感御姐 4、制服诱惑'))
page=1
url1="https://dimgw.us/xinggan/page/"+str(page) # 性感美女
url2="https://dimgw.us/qc/page/"+str(page) # 清纯可爱
url3="https://dimgw.us/yj/page/"+str(page) # 性感御姐
url4="https://dimgw.us/zf/page/"+str(page) # 制服诱惑
if ms==1:
url=url1
elif ms==2:
url=url2
elif ms==3:
url=url3
elif ms==4:
url=url4
else:
print('输入有误,请重新输入')
```
- 我感觉这部分不太优雅,这样写应该也一样:
```py
urls = ['xinggan', 'qc', 'yj', 'zf']
try:
ms=int(input('请输入爬取内容:1、性感美女 2、清纯可爱 3、性感御姐 4、制服诱惑'))
url = 'https://dimgw.us/' + urls
except ValueError:
print('请输入一个整数,重新输入:')
except IndexError:
print('输入有误,请重新输入:')
except:
print('未知错误')
```
- 没看明白 `url1="https://dimgw.us/xinggan/page/"+str(page)` ,四个 url 字符串为什么要拼接一个 `str(page)`?而且 `page` 是个常量
# 2
- 我习惯把`requests`发送请求部分写成一个函数,像这个程序中有 3 次请求,第三次是下载图片,那我喜欢写成这样:
```py
def req(url, dire=True): # dire 就是 direction 的简写,我发明的(自己造的)
headersvalue = {}
r = requests.get(url, headers=headersvalue)
sc = BeautifulSoup(r.text, 'lxml') if True else r.content # sc 就是 soup 和 content 的简写,我发明的(自己造的)
return sc
```
# 3. for 循环中的一些细节
- `a_tag` 中每个元素下只有一个 `a` 节点,可以用 `find()` 方法,直接返回 `Tag` 对象,随取随用,不然 `find_all()` 方法返回一个列表,还要从列表中取出来,多此一举。不过,像下面那样 `str()`获得网址的方式,倒是无所谓。这个相信你也知道,不过我见看见了就说出来,显得内容多不是,嘿嘿
- 获取 `c_tag` 的方式有些独特,没见过也没想到的方式,竟然把 `Tag` 对象转成字符串,然后切片。初见有些惊讶,细想却感觉粗糙,一般不是通过属性值得到网址吗?我在网上见过的源码也都是这样做的。就像后面下载图片那部分你写的,用`get()`方法,不过下面注释里那条语句好像写错了,应该是 `href=b_tag.get('href')`。这样的话,合起来就是:`url = child_href.find('a')['href']`
- `bs4` 里获取节点属性的方法除了`get()`,还有 `Tag` 对象的`attrs`属性,这两天我又发现了一种更简便的方法,就是上面`url = child_href.find('a')['href']`中用的:`Tag[属性值]`。分享一下,不知道你知道不知道
- 下载图片时的命名问题(个人向):用 img 节点的 title 属性命名不好吗,图片网址中一堆字母和数字的名字没什么作用,传递不了什么有用的信息
- 用 with 语句打开文件,就不用用 close() 方法关闭文件了,会自动关上,python官方文档上有个地方有写
# 4
`while True`部分,最后的`page += 1`什么作用啊?这个循环体内好像没有和 page 这个变量有关的东西,整个 `while True`好像是个死循环。我没运行整个代码,但应该就是死循环,因为 VS Code 提示了,程序最后一行的 print 语句 Code is unreachable
# 总结
我按照自己的习惯,写了一个,讨论一下:(变量名尽量按照你的来,方便对照)
```py
import requests
from bs4 import BeautifulSoup
def req(url, dire=True):
headersvalue = {
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36",
"Referer":"https://dimgw.us/zf"
}
r = requests.get(url, headers=headersvalue)
sc = BeautifulSoup(r.text, 'lxml') if dire else r.content
return sc
urls = ['xinggan', 'qc', 'yj', 'zf']
try:
ms=int(input('请输入爬取内容:1、性感美女 2、清纯可爱 3、性感御姐 4、制服诱惑'))
url = 'https://dimgw.us/' + urls
except ValueError:
print('请输入一个整数,重新输入:')
except IndexError:
print('输入有误,请重新输入:')
except:
print('未知错误')
a_tag = req(url)('h2',class_="entry-title")
for child_href in a_tag:
c_tag = child_href.find('a')['href']
div = req(c_tag).find('div',class_="entry-content u-text-format u-clearfix")('img')
for it in div:
img_url, img_name = it['src'], it['title']
with open(f'd:/test/{img_name}.webp','wb') as f:
f.write(req(img_url, dire=False))
print(img_name + '下载完成')
print('下载完成')
``` Ace803 发表于 2022-4-24 12:18
第一个问题,是我确实懒得写排除了,就是凑合一下,但是你写的代码比我漂亮多了,也学习了
第二个问题中 ...
这是我学 bs4 写的笔记,一起学习。Tag 对象的 attrs 属性里面有介绍
> https://beautifulsoup.readthedocs.io/zh_CN/v4.4.0/#
> https://beautifulsoup.cn/
- `beautifulsoup4`库也称为`Beautiful Soup`库或`bs4`库,用于解析 HTML 或 XML 文档
- `beautifulsoup4`库是一个第三方库
- `beautifulsoup4`库可以自动将输入文档转换为 Unicode 编码,将输出文档转换为 UTF-8 编码,故**不需要考虑编码方式**
- `beautifulsoup4`库在解析网页时需要依赖解析器,支持的解析器:
| 解析器 | 使用方法 | 优点 | 缺点 |
| --------------------- | ----------------------------------- | -------------------------------- | ------------------------------ |
| python 标准库中的 HTML 解析器 | BeautifulSoup(markup,'html.parser') | python 的内置标准库,执行速度适中,文档容错能力强 | python 2.73 或3.2.2 前的版本文档容错能力差 |
| lxml HTML 解析器 | BeautifulSoup(markup,'lxml') | 速度快,文档容错能力强 | 需要安装 C 语言库 |
| lxml XML 解析器 | BeautifulSoup(markup,'xml') | 速度快,唯一支持 XML 的解析器 | 需要安装 C 语言库 |
| html5lib | BeautifulSoup(markup,'html5lib') | 容错性最好,以浏览器的方式解析文档,生成 HTML5 格式的文档 | 速度慢,不依赖外部扩展 |
# 4 个对象
- beautiful soup 将复杂 HTML 文档转换成一个复杂的树形结构,每个节点都是 python 对象,所有对象可归纳为 4 种:Tag, NavigableString, BeautifulSoup, Comment
## Tag 对象
- 通俗来讲, Tag 就是 HTML 中的一个个标签
- 可以利用 `Tag 对象.标签名` 的方式获取标签的内容,查找的是所有内容中**第一个**符合要求的标签
- 可以利用 `Tag 对象[属性名]`的方式获取标签的属性值,返回一个 `list`
- Tag 对象有 4 个常用属性:
| 属性 | 说明 |
| -------- | --------------------------------------------- |
| name | 标签的名字,如 head、title 等,返回一个字符串 |
| string | 标签所包围的文字,网页中真实的文字,返回一个字符串 |
| attrs | 字典,包含了页面标签的所有属性,返回一个字典,可通过`attrs['属性名']`获取属性值 |
| contents | 这个标签下所有**子标签**的内容(HTML 源码),返回一个列表 |
- 按照 HTML 语法,可以在标签中嵌套其他标签,因此`string`属性的返回值遵遵循如下原则:
1. 如果标签内部没有其他标签, string 属性返回其中的内容
2. 如果标签内部还有其他标签,但只有一个标签, string 属性返回最里面标签的内容
3. 如果标签内部还有其他标签,且不止一个标签, string 属性返回 None
此外,还有一些属性:
| 属性 | 说明 |
| ----------------- | --------- |
| children | 返回所有的子节点|
| desecndants | 返回所有子孙节点|
| parent | 返回父节点 |
| parents | 返回父节点的父节点 |
| next_siblings | |
| previous_siblings | |
- .content 属性和 .childern 属性获取直接子节点,前者返回一个 list,后者返回一个 list 生成器对象,可以通过遍历获取所有子节点:
```py
for child in soup.body.children:
print(child)
```
> .desecndants 属性也返回一个 list 生成器对象
## NavigableString 对象
- Tag 对象的 string 属性返回的内容就是 NavigableString 对象
## BeautifulSoup 对象
- BeautifulSoup 对象表示的是一个文档的全部内容。大部分时候可以当作 Tag 对象,是一个特殊的 Tag
## Comment 对象
- Comment 注释对象是一个特殊类型的 NavigableString 对象,其内容不包括注释符号
```py
>>> from bs4 import BeautifulSoup
>>> soup = BeautifulSoup('<b><!--This is comment--></b>')
>>> print(type(soup.find('b').string))
<class 'bs4.element.Comment'>
```
- 解析网页时:
1. 需要使用`BeautifulSoup()`创建一个`BeautifulSoup`对象
> 该对象是一个树形结构,包含了 HTML 页面中的标签元素,如 <head>、<body> 等。也就是说,HTML 中的主要结构都变成了`BeautifulSoup`对象的一个个属性
2. 然后可通过`对象名.属性名`的形式获取该对象的**第一个属性值**(即节点)
> `BeautifulSoup`对象的属性名与 HTML 的标签名相同
# 搜索文档树
## find_all
find_all(name, attrs, recursive, string, limit)
> find_all() 方法搜索当前 tag 的所有 tag 子节点,并判断是否符合过滤器的条件
1. name:通过 HTML 标签名直接查找节点。如果 name 参数是正则表达式,则通过正则表达式的 match() 来匹配内容
2. attrs:通过 HTML 标签的属性查找结点(需列出属性名和值),可以同时设置多个属性
3. recursive:搜索层次,默认查找当前标签的所有子孙节点,如果只查找标签的子节点,可以使用参数 `recursive=False`
4. string:通过关键字检索 string 属性内容,传入的形式可以是字符串,也可以是正则表达式对象
5. limit:返回结果的个数,默认返回全部结果
> `find_all()`方法通过属性查找结点时,对于一些常用的属性(如 id 和 class 等),可以不用 attrs 字典形式来传递,而用复制的形式直接传入参数(如`find_all(class_='hotnews')`,由于 class 在 python 中是一个关键字,所以后面需要加一个下划线
- 由于 find_all() 方法非常常用,所以在 bs4 中,BeautifulSoup 对象和 tag 对象可以被当作一个 find_all() 方法使用,即 bs.find_all('a') 和 bs('a') 是等效的,soup.title.find_all(text='abc') 和 soup.title(text='abc') 是等效的
```py
import requests
from bs4 import BeautifulSoup
import re
url = ''
r = requests.get(url)
soup = BeautifulSoup(r.text, 'lxml')
print('所有 span 节点个数:', len(soup.find_all('span')))
print('class 属性值为 “amount” 的所有 span 节点:')
for node in soup.find_all('span', attrs={'class': 'amount'}):
print(node)
print('前 3 个 class 属性值为 “amount” 的 span 节点:\n', soup.find_all('span', attrs={'class': 'amount'}, limits=3))
print('string 属性值包含 “2室1厅” 的前三个节点的文本:', soup.find_all(string=re.compile('2室1厅'), limit=3))
```
## find
- `find()`方法的用法与`find_all()`方法相似,但其返回结果是第一个匹配的节点
## 其他查询方法
- `find_parents()`和`find_parent()`:前者返回所有祖先节点,后者返回直接父节点
- `find_next_siblings()`和`find_next_sibling()`:前者返回后面所有的兄弟节点,后者返回后面的第一个兄弟节点
- `find_previous_siblings()`和`find_previous_sibling()`:前者返回前面所有的兄弟节点,后者返回前面的第一个兄弟节点
- `find_all_previous()`和`find_previous()`:前者返回节点前所有符合条件的节点,后者返回节点前第一个符合条件的节点
## 多值属性
- HTML 中定义了一系列可以包含多个值的属性,最常见的多值属性是 class ,还有 rel, rev, accept-charset, headers, accesskey
- 在 Beautiful Soup 中多值属性的返回类型是 `list`
```py
soup = BeautifulSoup('<p class="body strikeout"></p>')
soup.p['class']
# ["body", "strikeout"]
soup = BeautifulSoup('<p class="body"></p>')
soup.p['class']
# ["body"]
```
- 如果某个属性值看起来好像有多个值,但在任何版本的 HTML 定义中都没有被定义为多值属性,那么 Beautiful Soup 会将这个属性组为字符串返回
```py
id_soup = BeautifulSoup('<p id="my id"></p>')
id_soup.p['id']
# 'my id'
```
- `find_all()`和`find()`方法中,根据属性筛选节点时,若某个节点的多值属性的一个属性值与给出的属性值相同,也会出现在结果中
# CSS 选择器
- `beautifulsoup4`库还提供了使用 CSS 选择器来选择节点的方法(调用`select()`方法传入相应的 CSS 选择器,返回一个 list,用 get_text() 方法或 text 属性获取文本)
- CSS 常用的选择器:
| 选择器 | 示例 | 示例说明 |
| ------------------ | --------------- | --------------------------------- |
| .class | .intro | 选择`class="intro"`的所有节点 |
| #id | #firstname | 选择`id="firstname"的所有节点 |
| * | * | 选择所有节点 |
| element | p | 选择所有`<p>`节点 |
| element,element | div,p | 选择所有`<dic>`节点和所有`<p>`节点 |
| element element | div p | 选择`<div>`节点内部的所有`<p>`节点 |
| element>element | div>p | 选择父节点为`<div>`节点的所有`<p>`节点 |
| | | 选择带有 target 属性的所有节点 |
| | | 选择 target="blank" 的所有节点 |
| :link | a:link | 选择所有未访问的链接 |
| :visited | a:visited | 选择所有已访问的链接 |
| :active | a:active | 选择活动链接 |
| :first-line | p:first-line | 选择每个`<p>`节点的首行 |
| element1~element2| p~ul | 选择前面有`<p>`节点的所有`<ul>`节点 |
| | a | 选择其 src 属性值以 “https” 开头的所有`<a>`节点 |
| | a| 选择其 src 属性值以 “.pdf” 结尾的所有`<a>`节点|
| | a | 选择其 src 属性值中包含 “abc” 字串的所有`<a>`节点 |
| :enabled | input:enabled | 选择每个启用的`<input>`节点 |
| :disabled | input:disabled| 选择每个禁用的`<input>`节点 |
| :checked | input:checked | 选择每个被选中的`<input>`节点 |
```py
import requests
from bs4 import BeautifulSoup
url = ''
r = requests.get(url)
soup = BeautifulSoup(r.text, 'lxml')
print('div 节点下所有 a 结点的个数:', len(soup.select('div a')))
# 获取第一个 calss 属性值为 “list-main-header” 的节点下所有 a 节点
node_a = soup.select('.list-main-header').select('a')
print('第一个 a 节点的 href 属性值:', node_a['href'])
print('第一个 a 节点的文本:', node_a.string)
```
# 实例
- 在 当当网 搜索 python,爬取该网页中图书的书名、作者、出版社和价格,保存到本地
```py
import requests
from bs4 import BeautifulSoup
def req(url):
headersvalue = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36 Edg/99.0.1150.52'}
r = requests.get(url, headers=headersvalue)
soup = BeautifulSoup(r.text, 'lxml')
return soup
url = 'http://search.dangdang.com/?key=python&act=input'
node_li = req(url).find('ul', {'class': 'bigimg'}).select('li')
book_detail = []
for li in node_li:
name = li.a.attrs['title']
author = li.find('p', class_='search_book_author').span.a.attrs['title']
publisher = li.find('a', dd_name = '单品出版社').string
price = li.find('span', class_='search_now_price').string
book_detail.append('{}, {}, {}, {}'.format(name, author, publisher, price))
with open('d:/book_detail.txt', 'w', encoding='utf-8') as f:
f.write('\n'.join(book_detail))
``` 这个好好玩啊 请问朋友是在哪里自学的,我也想学爬虫,你有好的教程推荐吗? yuqilin234 发表于 2022-4-23 21:53
这个好好玩啊
从早上9点,到晚上9点多,写了两个爬虫,这是其中之一,另外一个是小说的爬虫,一天都在解决bug和报错了{:301_1008:} 414246704 发表于 2022-4-23 21:57
请问朋友是在哪里自学的,我也想学爬虫,你有好的教程推荐吗?
先在b站看的基础的,是杨淑娟老师讲的,挺好懂得,只是自己练习还是挺难的
然后又看的爬虫教学,看的头都大了
现在自己试着写些爬虫,头要爆炸了 谢谢,辛苦了。顶一个。 这个网站良心啊,不用登陆都能直接下载压缩包 wuaiwuai888 发表于 2022-4-23 22:16
这个网站良心啊,不用登陆都能直接下载压缩包
是的,主要是练手{:301_995:} 哦这东西蛮有趣的 这个挺好的