哈岁NB 发表于 2023-5-30 10:54:50

爬取图片

请问大佬们,这个代码怎样修改一下才能把图片都下载下来呢
import requests
import logging
import re
from urllib.parse import urljoin
from os.path import exists
from os import makedirs

result_dir = 'tu'
exists(result_dir) or makedirs(result_dir)


logging.basicConfig(level=logging.INFO,format = '%(asctime)s - %(levelname)s: %(message)s')

header = {
   "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36"
}
url = 'http://md.itlun.cn/a/nhtp/list_2_{}.html'
pages = 3


def scrpae_page(url):
    logging.info('scraping %s...', url)
    try:
      response = requests.get(url=url,headers=header)
      if response.status_code == 200:
            response.encoding = 'gbk'
            return response
      logging.error('get invalid status code %s while scrape %s',response.status_code,url)
    except requests.RequestException:
      logging.error('error occurred while scraping %s',url,exc_info=True)

#拼接URl,并爬取主页
def scrape_index(page):
    index_url = url.format(page)
    return scrpae_page(index_url)

#解析详情页url
def parse_index(html):
    #logging.info('{}'.format(html))
    url_pattern = re.compile('<script.*?src = "(.*?)"; </script>',re.S)
    items = re.findall(url_pattern,html)
    title_pattern = re.compile('<script.*?<span>(.*?)</span>.*?</script>',re.S)
    titles = re.findall(title_pattern,html)
    # logging.info('{}'.format(list(titles)))

    return {
      '名称':titles,
      'detail_url':items
    }

#拼接url,并调用scrpae_page
def parse_url(dict):
    for base_url in dict:
      detail_url = urljoin(url,base_url)
      return scrpae_page(detail_url).content

#保存
def save(title,conect):
    for t in title:
      img_path = result_dir + '/' + t + '.jpg'
      with open(img_path,'wb') as fp:
            fp.write(conect)



def main():
    for page in range(1,pages):
      index_html = scrape_index(page)
      title = parse_index(index_html.text)['名称']
      details_url = parse_index(index_html.text)['detail_url']
      conect = parse_url(details_url)
      save(title,conect)
      logging.info('保存成功')



if __name__ == '__main__':
    main()

isdkz 发表于 2023-5-30 12:35:12

import requests
import logging
import re
from urllib.parse import urljoin
from os.path import exists
from os import makedirs

result_dir = 'tu'
exists(result_dir) or makedirs(result_dir)


logging.basicConfig(level=logging.INFO,format = '%(asctime)s - %(levelname)s: %(message)s')

header = {
   "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36"
}
url = 'http://md.itlun.cn/a/nhtp/list_2_{}.html'
pages = 3


def scrpae_page(url):
    logging.info('scraping %s...', url)
    try:
      response = requests.get(url=url,headers=header)
      if response.status_code == 200:
            response.encoding = 'gbk'
            return response
      logging.error('get invalid status code %s while scrape %s',response.status_code,url)
    except requests.RequestException:
      logging.error('error occurred while scraping %s',url,exc_info=True)

#拼接URl,并爬取主页
def scrape_index(page):
    index_url = url.format(page)
    return scrpae_page(index_url)

#解析详情页url
def parse_index(html):
    #logging.info('{}'.format(html))
    url_pattern = re.compile('<script.*?src = "(.*?)"; </script>',re.S)
    items = re.findall(url_pattern,html)
    title_pattern = re.compile('<LI>.*?><IMG id="img.*?><span>(.*?)</span></a></LI>',re.S)               # 修改了正则表达式
    titles = re.findall(title_pattern,html)
    # logging.info('{}'.format(list(titles)))
    return {
      '名称':titles,
      'detail_url':items
    }

#拼接url,并调用scrpae_page
def parse_url(path):
    detail_url = urljoin(url, path)
    return scrpae_page(detail_url).content

#保存
def save(title,conect):
    invalid_chars = ['\\', '/', ':', '*', '?', '"', '<', '>', '|']# Invalid Windows characters
    for char in invalid_chars:
      title = title.replace(char, '')

    img_path = result_dir + '/' + title + '.jpg'
    with open(img_path,'wb') as fp:
      fp.write(conect)



def main():
    for page in range(1,pages):
      index_html = scrape_index(page)
      titles = parse_index(index_html.text)['名称']
      details_url = parse_index(index_html.text)['detail_url']
      for title, url in zip(titles, details_url):
            conect = parse_url(url)
            save(title,conect)
            logging.info('保存成功')



if __name__ == '__main__':
    main()


在修改后的代码中,我主要进行了以下几个方面的改动:

1. 解析详情页url的函数(parse_index):
我发现原先代码中的正则表达式可能无法正确匹配网页中的标题数据,所以我对其进行了修改。原本的表达式是 '<script.*?<span>(.*?)</span>.*?</script>',我修改为了 '<LI>.*?><IMG id="img.*?><span>(.*?)</span></a></LI>',这个正则表达式能够更精确地匹配网页中的标题数据。

2. 拼接URL并调用scrpae_page的函数(parse_url):
原先的代码中,这个函数接收的参数是一个字典,但是实际上我们只需要传入一个URL路径即可。因此我将这个函数的参数从字典改为了单个URL路径,这样处理后,函数的实现逻辑也变得更加清晰简单了。

3. 图片保存函数(save):
我注意到在保存图片文件的时候,如果标题中含有Windows系统不支持的特殊字符,会导致保存失败。因此,我加入了一个特殊字符的检查和替换过程。通过一个列表invalid_chars,我列出了Windows系统中文件名不支持的特殊字符,然后遍历这个列表,如果标题中含有这些字符,就将其替换为空字符。

4. 主函数(main):
我发现原来的代码中只保存了每页第一个项目的图片,而没有处理一个页面上的所有项目。因此,我在主函数中添加了一个内部的for循环,遍历每一页中的所有标题和链接,并进行下载保存,这样就可以完整地保存每一页中的所有图片了。


天地丶良心 发表于 2023-5-30 14:25:23

1.第12行将result_dir改为result_path
2.将14行的判断条件改为if not exists(result_path): makedirs(result_path)
3.第20行的Chrome版本号应该改为实际版本号
4.第28行将scrpae_page修改为scrape_page
5.第32行的url.join应该改为urljoin
6.第40行的connect应该改为content
7.第44行的保存成功应该放在for循环里面,每次保存完都输出一次
8.第43行应该先判断conect是否为空,如果不为空再进行保存

修改后代码如下:


import requests
import logging
import re
from urllib.parse import urljoin
from os.path import exists
from os import makedirs

result_path = 'tu'
if not exists(result_path):
makedirs(result_path)

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s: %(message)s')

headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36"
}
url = 'http://md.itlun.cn/a/nhtp/list_2_{}.html'
pages = 3


def scrape_page(url):
logging.info('正在爬取 %s...', url)
try:
response = requests.get(url=url, headers=headers)
if response.status_code == 200:
response.encoding = 'gbk'
return response
logging.error('爬取 %s 时返回了错误的状态码 %s', url, response.status_code)
except requests.RequestException:
logging.error('爬取 %s 时发生错误', url, exc_info=True)


#获取主页面内容
def scrape_index(page):
index_url = url.format(page)
return scrape_page(index_url)


# 解析详情页url
def parse_index(html):
url_pattern = re.compile('', re.S)
items = re.findall(url_pattern, html)
title_pattern = re.compile('(.*?).*?', re.S)
titles = re.findall(title_pattern, html)

return {
'名称': titles,
'detail_url': items
}


#拼接url,并调用scrapte_page方法
def parse_url(detail_url):
for base_url in detail_url:
detail_url = urljoin(url, base_url)
content = scrape_page(detail_url).content
if content:
save(parse_index(detail_url)['名称'], content)


#保存文件
def save(title_list, content):
for title in title_list:
img_path = result_path + '/' + title + '.jpg'
with open(img_path, 'wb') as f:
f.write(content)
logging.info('已保存%s张图片', len(title_list))


def main():
for page in range(1, pages):
index_html = scrape_index(page)
detail_url = parse_index(index_html.text)['detail_url']
parse_url(detail_url)


if __name__ == '__main__':
main()

哈岁NB 发表于 2023-5-30 15:19:22

isdkz 发表于 2023-5-30 12:35
在修改后的代码中,我主要进行了以下几个方面的改动:

1. 解析详情页url的函数(parse_index):


好的,感谢感谢

哈岁NB 发表于 2023-5-30 15:19:54

天地丶良心 发表于 2023-5-30 14:25
1.第12行将result_dir改为result_path
2.将14行的判断条件改为if not exists(result_path): makedirs(resu ...

好的,感谢感谢

哈岁NB 发表于 2023-5-30 15:34:26

isdkz 发表于 2023-5-30 12:35
在修改后的代码中,我主要进行了以下几个方面的改动:

1. 解析详情页url的函数(parse_index):


请问一下,如果想改成多线程该怎么改呢

isdkz 发表于 2023-5-30 16:15:37

哈岁NB 发表于 2023-5-30 15:34
请问一下,如果想改成多线程该怎么改呢

对于你的爬虫代码,可以使用Python的线程池 concurrent.futures.ThreadPoolExecutor 来并发的运行你的任务。下面是一个示例:

import requests
import logging
import re
from urllib.parse import urljoin
from os.path import exists
from os import makedirs
from concurrent.futures import ThreadPoolExecutor

result_dir = 'tu'
exists(result_dir) or makedirs(result_dir)

logging.basicConfig(level=logging.INFO,format = '%(asctime)s - %(levelname)s: %(message)s')

header = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36"
}
url = 'http://md.itlun.cn/a/nhtp/list_2_{}.html'
pages = 3


def scrape_page(url):
    logging.info('scraping %s...', url)
    try:
      response = requests.get(url=url,headers=header)
      if response.status_code == 200:
            response.encoding = 'gbk'
            return response
      logging.error('get invalid status code %s while scrape %s',response.status_code,url)
    except requests.RequestException:
      logging.error('error occurred while scraping %s',url,exc_info=True)


def scrape_index(page):
    index_url = url.format(page)
    return scrape_page(index_url)


def parse_index(html):
    url_pattern = re.compile('<script.*?src = "(.*?)"; </script>',re.S)
    items = re.findall(url_pattern,html)
    title_pattern = re.compile('<LI>.*?><IMG id="img.*?><span>(.*?)</span></a></LI>',re.S)
    titles = re.findall(title_pattern,html)
    return {
      '名称':titles,
      'detail_url':items
    }


def parse_url(path):
    detail_url = urljoin(url, path)
    return scrape_page(detail_url).content


def save(title,content):
    invalid_chars = ['\\', '/', ':', '*', '?', '"', '<', '>', '|']# Invalid Windows characters
    for char in invalid_chars:
      title = title.replace(char, '')

    img_path = result_dir + '/' + title + '.jpg'
    with open(img_path,'wb') as fp:
      fp.write(content)
    logging.info('保存成功')


def main():
    with ThreadPoolExecutor(max_workers=4) as executor:# 更改max_workers的值来改变线程池中线程的数量
      for page in range(1, pages):
            index_html = scrape_index(page)
            titles = parse_index(index_html.text)['名称']
            details_url = parse_index(index_html.text)['detail_url']
            for title, url in zip(titles, details_url):
                executor.submit(save, title, parse_url(url))# 提交任务到线程池
               


if __name__ == '__main__':
    main()


这个版本的代码使用了一个线程池,其中包含4个工作线程(这个数量可以调整),并且使用 executor.submit 来提交任务到线程池。 save 和 parse_url 函数将并行执行,这将显著提高你的爬虫程序的速度。

注意,使用多线程并不总是可以提高爬虫的速度,这取决于你的代码是否主要受I/O(例如网络请求)影响

,以及你的目标网站是否允许大量并发请求。如果你的爬虫主要受CPU限制(例如,大量的数据处理或计算),或者你的目标网站对并发请求有严格的限制,那么增加线程数可能不会有太大的帮助,甚至可能对你的爬虫产生负面影响。

天地丶良心 发表于 2023-5-30 16:18:36

哈岁NB 发表于 2023-5-30 15:34
请问一下,如果想改成多线程该怎么改呢

可以通过使用Python中的多线程模块进行改进。具体来说,可以将主体循环程序放在一个线程中,然后在另一个线程中定义一个函数,获取并解析详情页url,并调用scrapte_page方法进行爬取和保存文件。多个线程同时进行这个过程,从而提高爬取速度。
下面是修改后的代码示例:

import requests
import logging
import re
from urllib.parse import urljoin
from os.path import exists
from os import makedirs
import threading

result_path = 'tu'
if not exists(result_path):
makedirs(result_path)

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s: %(message)s')

headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36"
}
url = 'http://md.itlun.cn/a/nhtp/list_2_{}.html'
pages = 3

def scrape_page(url):
logging.info('正在爬取 %s...', url)
try:
response = requests.get(url=url, headers=headers)
if response.status_code == 200:
response.encoding = 'gbk'
return response
logging.error('爬取 %s 时返回了错误的状态码 %s', url, response.status_code)
except requests.RequestException:
logging.error('爬取 %s 时发生错误', url, exc_info=True)

#获取主页面内容
def scrape_index(page):
index_url = url.format(page)
return scrape_page(index_url)

# 解析详情页url
def parse_index(html):
url_pattern = re.compile('', re.S)
items = re.findall(url_pattern, html)
title_pattern = re.compile('(.*?)', re.S)
titles = re.findall(title_pattern, html)

return {
'名称': titles,
'detail_url': items
}

#拼接url,并调用scrapte_page方法
def parse_url(detail_url):
for base_url in detail_url:
detail_url = urljoin(url, base_url)
content = scrape_page(detail_url).content
if content:
save(parse_index(detail_url)['名称'], content)

#保存文件
def save(title_list, content):
for title in title_list:
img_path = result_path + '/' + title + '.jpg'
with open(img_path, 'wb') as f:
f.write(content)
logging.info('已保存%s张图片', len(title_list))

def worker(page):
index_html = scrape_index(page)
detail_url = parse_index(index_html.text)['detail_url']
parse_url(detail_url)

def main():
threads = []
for page in range(1, pages):
t = threading.Thread(target=worker, args=(page,))
threads.append(t)
t.start()

for t in threads:
t.join()

if __name__ == '__main__':
main()

isdkz 发表于 2023-5-30 16:22:21

天地丶良心 发表于 2023-5-30 14:25
1.第12行将result_dir改为result_path
2.将14行的判断条件改为if not exists(result_path): makedirs(resu ...

你是认真的吗{:5_94:}

天地丶良心 发表于 2023-5-30 16:28:20

isdkz 发表于 2023-5-30 16:22
你是认真的吗

{:5_102:}试试就知道了

isdkz 发表于 2023-5-30 16:29:50

天地丶良心 发表于 2023-5-30 16:28
试试就知道了

试了,完全行不通

天地丶良心 发表于 2023-5-30 16:30:38

那就不知道了

isdkz 发表于 2023-5-30 16:39:05

天地丶良心 发表于 2023-5-30 16:30
那就不知道了

不知道你还发

天地丶良心 发表于 2023-5-30 16:43:14

isdkz 发表于 2023-5-30 16:39
不知道你还发

错的不能发?谁规定不能发?

isdkz 发表于 2023-5-30 16:47:19

天地丶良心 发表于 2023-5-30 16:43
错的不能发?谁规定不能发?

那你也不能瞎发呀,自己都没试过的还让别人试,混淆视听,都不知道你这从哪搞来的就发出来

也不像chatgpt,chatgpt不会错的这么离谱的

天地丶良心 发表于 2023-5-30 16:53:01

哈岁NB 发表于 2023-5-30 15:34
请问一下,如果想改成多线程该怎么改呢

你可以使用 Python 的 `concurrent.futures` 库。
以下是修改后的代码:

```python
import requests
import logging
import re
from urllib.parse import urljoin
from os.path import exists
from os import makedirs
from concurrent.futures import ThreadPoolExecutor

result_path = 'tu'
if not exists(result_path):
    makedirs(result_path)

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s: %(message)s')

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36"
}
url = 'http://md.itlun.cn/a/nhtp/list_2_{}.html'
pages = 3

def scrape_page(url):
    # ... (same as before)

def scrape_index(page):
    # ... (same as before)

def parse_index(html):
    # ... (same as before)

def parse_url(detail_url):
    # ... (same as before)

def save(title_list, content):
    # ... (same as before)

def process_page(page):
    index_html = scrape_index(page)
    detail_url = parse_index(index_html.text)['detail_url']
    parse_url(detail_url)

def main():
    with ThreadPoolExecutor() as executor:
      executor.map(process_page, range(1, pages + 1))

if __name__ == '__main__':
    main()
```

这里的主要更改是:

1. 导入 `ThreadPoolExecutor` 类。
2. 创建一个新的 `process_page` 函数,它将处理每个页面的爬取和解析。
3. 在 `main` 函数中,使用 `ThreadPoolExecutor` 创建一个线程池,并使用 `executor.map` 方法将 `process_page` 函数应用到每个页面。

这样,程序将使用多线程并行处理每个页面,从而提高爬虫的速度。你可以根据需要调整线程池的大小。

哈岁NB 发表于 2023-5-30 16:59:45

isdkz 发表于 2023-5-30 16:15
对于你的爬虫代码,可以使用Python的线程池 concurrent.futures.ThreadPoolExecutor 来并发的运行你的任 ...

好的,感谢感谢

天地丶良心 发表于 2023-5-30 16:59:58

isdkz 发表于 2023-5-30 16:47
那你也不能瞎发呀,自己都没试过的还让别人试,混淆视听,都不知道你这从哪搞来的就发出来

也不像chat ...

从太上老君的炼丹炉偷来的{:5_109:}

哈岁NB 发表于 2023-5-30 17:00:16

天地丶良心 发表于 2023-5-30 16:18
可以通过使用Python中的多线程模块进行改进。具体来说,可以将主体循环程序放在一个线程中,然后在另一个 ...

好的,感谢感谢

天地丶良心 发表于 2023-5-30 17:03:42

isdkz 发表于 2023-5-30 16:47
那你也不能瞎发呀,自己都没试过的还让别人试,混淆视听,都不知道你这从哪搞来的就发出来

也不像chat ...

16楼是用孙悟空的金箍棒变出来的,还有错误吗?
页: [1] 2
查看完整版本: 爬取图片