【学习交流】python简易爬虫入门

我上面写的就是可以套用的模板。配合 cURL 转换器一起使用,只需要复制粘贴就可以了。

谢谢,我再试试。

你举这个例子不是最简单的,网站有简单的反爬机制,要设置请求头中User-Agent和cookie才行

1 个赞

那换成百度百科呢

https://baike.baidu.com/item/一
https://baike.baidu.com/item/二
https://baike.baidu.com/item/三
https://baike.baidu.com/item/四
https://baike.baidu.com/item/五
https://baike.baidu.com/item/六
https://baike.baidu.com/item/七

import requests
url=‘百度百科-验证
headers={‘User-Agent’: ‘Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0’}
r=requests.get(url,headers=headers)
print(r.text)

代码直接粘上,网页自动更改了标点符号,复制到PY上不能用。这个是抓一个网页的。

今天又试了下,换成百度百科的地址,成功了。
不过地址得换成百分比,不能有汉字,如果是这样的:

https://baike.baidu.com/item/一
https://baike.baidu.com/item/二
https://baike.baidu.com/item/三

程序报错:

UnicodeDecodeError: 'gbk' codec can't decode byte 0x80 in position 31: illegal multibyte sequence

把地址中的汉字改为百分比编码:

https://baike.baidu.com/item/%e4%b8%80
https://baike.baidu.com/item/%e4%ba%8c
https://baike.baidu.com/item/%e4%b8%89
https://baike.baidu.com/item/%e5%9b%9b
https://baike.baidu.com/item/%e4%ba%94
https://baike.baidu.com/item/%e5%85%ad
https://baike.baidu.com/item/%e4%b8%83

保存为:address.txt,放在PY文件夹内
再改一下输出文件的编码,否则还是会报错:

UnicodeEncodeError: 'gbk' codec can't encode character '

我改后的代码是这样的

# -*- coding: utf-8 -*-
import requests
import os.path
from os import path
import time

for i, line in enumerate(open("address2.txt")):
    filename = str(i) + ".html"  # 保存的文件名

    # 检查文件是否存在,存在跳过
    if path.exists(filename):
        continue

    headers = {
        'authority': 'https://baike.baidu.com/item/%e4%b8%80',
        'pragma': 'no-cache',
        'cache-control': 'no-cache',
        'upgrade-insecure-requests': '1',
        'dnt': '1',
        'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.4 Safari/605.1.15',
        'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
        'sec-fetch-site': 'same-origin',
        'sec-fetch-mode': 'navigate',
        'sec-fetch-dest': 'document',
        'referer': 'https://baike.baidu.com/item/%e4%b8%80',
        'accept-language': 'en,ja;q=0.9,zh-CN;q=0.8,zh;q=0.7',
        'cookie': 't=d910e897351658b915c67413eb1d4c2f; r=9132',
    }

    response = requests.get(line, headers=headers)

    # 打印文本行,去除前后空格换行,http状态码,响应内容长度
    print(i, line.strip(), response.status_code, len(
        response.text))

    # 发现会返回空文件,检查响应内容长度,大于1000,再保存文件
    if len(response.text) > 1000 and response.status_code == 200:
        with open(filename, "w",encoding='utf-8') as f:
            f.write(response.text)

    # 等待5秒
    #time.sleep(5)

成功跑了起来。
输出了0.html,1.html,…6.html,只是文件名自动从0开始编号了。
image

怎样让抓到的文件直接合并成一个TXT输出?地址多的话,单个文件太多了

又,如果是有规律的地址,如,最后的数字不同,递增的,从2到65,应该不要地址列表了吧,怎样改代码。用通配符?

http://www.yac8.com/news/10725_2.html
...
http://www.yac8.com/news/10725_65.html

此段成功运行。抓取到了网页。并保存为TXT。只是一个地址。

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page()
    page.goto("https://zidian.911cha.com/zi7684.html")
    r = page.content()
    with open(r'd:\XHZD.txt', 'ab') as fi:  # D:\XHZD.txt是抓到的内容存放的目录
        fi.write(r.encode())
    #time.sleep(random.random() * 6)
    browser.close()

print('end')

如何抓一个地址列表呢

1 个赞

还是建议大师系统学一遍,因为如果爬取遇到问题或想自定义部分功能都是需要改代码的。

正在摸索,現學現查現用。

建议用 playwright ,实际是开启一个新的 chrome 浏览器去爬取网页,被识别为爬虫的概率低些,动态加载的网页也能处理。循环爬取,供参考。
spider.py (1.7 KB)

# -*- coding: utf-8 -*-

import os.path
from os import path
import time
from playwright.sync_api import sync_playwright
from playwright.sync_api import TimeoutError

with sync_playwright() as p:
    browser = p.chromium.launch()

    # 模拟高清屏,2为缩放倍率,爬取网页可以用1,制作高清的图片词典可以用2
    context = browser.new_context(device_scale_factor=2)

    page = context.new_page()

    for i, line in enumerate(open("address.txt")):
        filename = str(i) + ".html"  # 保存的文件名
        line = line.strip()  # 移除文本行前后空格

        # 检查文件是否存在,存在跳过
        if path.exists(filename):
            continue

        try:
            # 设置50秒超时,默认是30秒,超时就跳过,下次再处理。
            page.set_default_navigation_timeout(50000)
            page.goto(line)
        except TimeoutError:
            # 打印文本行,去除前后空格换行,错误提示
            print('current: ', i, line, '[timeout]')
            continue

        # 等待2秒,确保动态网页也可以爬取
        time.sleep(2)

        # 读取网页内容
        content = page.content()
        # 打印文本行,去除前后空格换行,响应内容长度
        print('current: ', i, line, len(content))

        # 保存网页到文件
        with open(filename, "w") as f:
            f.write(content)

        # 保存截图,方便查看效果
        page.screenshot(path="screenshot.png", full_page=True)
        # 保存指定选择器的截图,如果网页加密,可以方便制作图片词典
        # elem = page.query_selector(".mtb")
        # elem.screenshot(path="mtb.png")

    browser.close()

3 个赞

正常操作,就应该下载完所有文件最后再合并,因为下载过程中会出很多问题的。另外使用文件保存地址列表,通过列表可以判断是否已经下载过,避免重复下载,后续会很方便。
merger.py (1.4 KB)

# -*- coding: utf-8 -*-

from pathlib import Path
from os import walk
from os.path import isfile, join

# 获取脚本当前所在目录
current_dir = Path(__file__).parent.absolute()
print("current_dir: ", current_dir)

# 获取子目录
sub_dir = current_dir.joinpath("output")
print("sub_dir: ", sub_dir)

def find_filenames(path, ext):
    filenames = []
    for home, dirs, files in walk(path):
        for filename in files:
            # 使用当前所在目录+文件名,组成完整文件路径
            filename = join(home, filename)
            # 检查是否是文件,并且以 '.html'结尾
            if isfile(filename) and filename.endswith(ext):
                # 打印文件名
                print("filename: ", filename)
                # 写入到 filenames 里
                filenames.append(filename)
    return filenames


# 读取 sub_dir 下所有目录及文件名
# 读取到的相关文件名(.html)保存在这个变量里
filenames = find_filenames(sub_dir, ".html")

# 准备写入的文件
with open("merged.txt", 'w') as f1:
    # 遍历前面读取到的文件名列表
    for filename in filenames:
        # 打开文件
        with open(filename, "r") as f2:
            # 读取文件的内容
            content = f2.read()
            # 移除换行符
            content = content.replace("\r\n", "\n")
            content = content.replace("\n", "")
            # 写入文件
            f1.write(content + "\n</>\n")

1 个赞

感謝,成功運行!得加編碼,要不然報錯。

with open(filename, "w", encoding='utf-8') as f:
1 个赞

這個問題怎麼寫?比如:http://www.yac8.com/news/1.html到http://www.yac8.com/news/9999.html,要抓取所有的1-9999網址。

可以拼接出更复杂的网址,供参考。

address.py (363 Bytes)

# -*- coding: utf-8 -*-

# 准备写入的文件
with open("address.txt", 'w') as f1:
    # range 中的2为起始数,10为终止数,但不会包括,1是步长
    for num in range(2, 10, 1):
        # 拼接地址
        content = "https://zidian.911cha.com/" + str(num) + ".html"
        # 写入文件,加上换行符
        f1.write(content + "\n")

1 个赞

哦,這樣先得到一個地址列表。然後再用以上方法。但是如果超級長,這樣似乎有些笨拙。比如幾百萬,幾千萬,百度百科就是這樣的地址。不能用通配符?像迅雷、IDM,可用通配符匹配地址。

先生成文件的地址列表,再去爬取确实笨拙,但实际用起来很方便。可以把复杂的任务,分解成小步骤,慢慢解决,降低难度。没有使用过迅雷 IDM 批量下载,不了解他们的工作方式。

按上面的方式处理百科,应该没问题的,先获取词头,再拼接出网址,最后去爬取,如果没有现成的词头那确实困难。

1 个赞

百度百科,22,287,159 个词条,100萬一個地址表,分成23份。也行。

1 个赞

怎么方便怎么来吧,都可以。