likes
comments
collection
share

爬虫:从传统编程到AIGC

作者站长头像
站长
· 阅读数 32

引言

随着人工智能技术的飞跃式发展,特别是生成式AI(AIGC)的兴起,爬虫技术正迎来一场前所未有的变革。本文将探讨传统编程下的爬虫技术,随后揭示AIGC如何为爬虫技术赋能,提升爬虫的效率与智能化水平。让我们一起开始探索AIGC引领下的爬虫技术新天地吧~

传统编程的爬虫

爬虫的底层本质

简单来说就是“他有我拿”

  • 向目标URL 以GET方式 发送一个http请求
  • 得到响应,这里的响应是html的字符串
  • 解析html字符串,通过css选择器拿到我们所需数据的核心片段
  • 对获取到的数据进行清洗、转换格式等处理,以JSON格式返回,供我们程序使用

接下来我们就以爬取一个 豆瓣TOP250电影列表(movie.douban.com/top250) 为例,为大家详细讲解传统编程的爬虫技术!

纯 JS

一、初始化为后端项目

npm init -y 命令初始化为你的项目为一个后端项目,它会在当前目录下创建一个 package.json 文件,并自动填写一些默认值,如项目名称、版本号、作者等。使用 -y 参数表示接受所有默认值,不需要用户手动输入。这样可以快速开始一个新项目,而无需手动配置 package.json 文件。

二、安装第三方包

用“npm i”命令安装我们编程中要用到的包,所以接下来我们需要执行npm i request-promise, npm i cheerio, npm i fs, npm i util命令(后面会讲这些包的作用)。

三、创建 main.js 入口文件

此时文件目录应该如下: (VSCode环境下)

爬虫:从传统编程到AIGC

main.js 完整代码:

let request = require('request-promise') 
let cheerio = require('cheerio') 
let fs = require('fs')   
const util = require('util')
let movies = []
let basicUrl = 'https://movie.douban.com/top250'
let once = function (cb) {
    let active = false
    if (!active) {
        cb()
        active = true
    }
}
function log(item) {
    once(() => {
        console.log(item)
    })
}
function getMovieInfo (node) {
    let $ = cheerio.load(node)
    let titles = $('.info .hd span')
    titles = ([]).map.call(titles, t => {
        return $(t).text()
    })
    let bd = $('.info .bd')
    let info = bd.find('p').text()
    let score = bd.find('.star .rating_num').text()
    return { titles, info, score }
}
async function getPage (url, num) {
    let html = await request({
      url
    })
    console.log('连接成功!', `正在爬取第${num+1}页数据`)
    let $ = cheerio.load(html)
    let movieNodes = $('#content .article .grid_view').find('.item')
    let movieList = ([]).map.call(movieNodes, node => {
      return getMovieInfo(node)
    })
    return movieList
}
async function main () {
    let count = 25
    let list = []
    for (let i = 0 ; i < count ; i++) {
        let url = basicUrl + `?start=${25*i}`
        list.push(... await getPage(url, i))
    }
    console.log(list.length)
    fs.writeFile('./output.json', JSON.stringify(list), 'utf-8', () => {
      console.log('生成json文件成功!')
    })
}
main()

详细解释

1. 导包

require()是Node.js中用于导入模块的语法,返回该模块导出的对象或函数。let request = require('request-promise') 引入http请求库,并将其赋值给request,之后可以用request函数发送请求,并返回响应内容; 引入cheerio库,将html字符串解析成内存中DOM对象,方便操作; 引入fs库,用于对文件系统进行增删查改等操作; 引入util库,这个模块包含一些用于开发Node.js应用程序的实用功能,这个例子中我们用到util.promisify将回调风格的函数转换为返回Promise的函数,从而更方便地与async/await一起使用

let request = require('request-promise') 
let cheerio = require('cheerio') 
let fs = require('fs')   
const util = require('util')

2. main函数

整个主函数要执行的操作就是循环把每条信息数组放入list中,并将这些数据存储到一个JSON文件中。

let count = 25:要爬取的信息有25页,每一页的URL都是不一样的,后期就要循环25次

list.push(... await getPage(url, i)):关键字await会暂停当前函数的执行,等待 getPage() 函数的完成并返回一个电影列表数组;...运算符将数组转换为一个用逗号分隔的参数序列,再被push到list数组中

fs.writeFile():写入文件(文件可以不存在,会自动添加改文件)。最后一条语句是它将 list 数组中的数据转换为 JSON 格式,并将其写入到同目录下名为 output.json 的文件中。一旦写入完成,会执行一个回调函数打印一个成功的日志消息。

async function main () {
    let count = 25
    let list = []
    for (let i = 0 ; i < count ; i++) {
        let url = basicUrl + `?start=${25*i}`
        list.push(... await getPage(url, i)) 
    }
    console.log(list.length)
    fs.writeFile('./output.json', JSON.stringify(list), 'utf-8', () => {
      console.log('生成json文件成功!')
    })
}

3. getPage函数

let html = await request({ url }):向url发送一个请求得到一个字符串即HTML的源代码,赋值给html

let $ = cheerio.load(html):cheerio 在内存中将html字符串实例化成DOM树并加载到$ 变量中,以便于后续的 DOM 操作

let movieNodes = $('#content .article .grid_view').find('.item') :使用CSS选择器 链式选择所有带有类名item的子元素,并将这些元素作为jQuery对象返回给movieNodes

小细节:这里不直接使用$('#content .article .grid_view .item')是因为这样只会在.grid_view的直接子元素中查找.item,而.find('.item')会查找其内部的所有.item类元素,不论与grid_view相隔几级

([]).map.call(movieNodes, node => {return getMovieInfo(node)}):movieNodes是一个类数组对象,不能直接使用数组的 map 方法,所以调用一个空数组的map方法,使用了 call 方法来改变 map 内部的 this 指向给movieNodes,以便能够遍历其中的每个元素。每遍历一次通过回调函数返回一个新的值给movieList数组

async function getPage (url, num) {
    let html = await request({
        url
    })
    console.log('连接成功!', `正在爬取第${num+1}页数据`)
    let $ = cheerio.load(html)
    let movieNodes = $('#content .article .grid_view').find('.item')  
    let movieList = ([]).map.call(movieNodes, node => {
        return getMovieInfo(node)
    })
    return movieList
}

4. getMovieInfo函数

该函数从给定DOM结点中解析出电影的标题 titles、详情 info、评分 score整理成一个对象并返回。

titles = ([]).map.call(titles, t => {return $(t).text()}):调用 text() 方法提取其文本内容,titles 最终成为了一个包含所有电影标题文本的数组

function getMovieInfo (node) {
    let $ = cheerio.load(node)
    let titles = $('.info .hd span')
    titles = ([]).map.call(titles, t => {
        return $(t).text() 
    })
    let bd = $('.info .bd')
    let info = bd.find('p').text()
    let score = bd.find('.star .rating_num').text()
    return { titles, info, score } 
}

AIGC爬虫

与传统编程的区别

拿到html字符串后,不需要去解析,而是把字符串直接交给大模型去处理并返回给我们一个JSON数组

Python + 大模型

一、申领API-KEY

访问dashscope.console.aliyun.com/,创建API-KEY,拿到后复制后续要用

爬虫:从传统编程到AIGC

二、安装第三方包

本例的编程平台是colab.research.google.com/drive (需要科学上网)

引入http请求库requests,引入bs4库用于解析html文档

!pip install requests
!pip install beautifulsoup4
!pip install dashscope

三、完整代码

import requests  # 引入请求库
import dashscope  
from bs4 import BeautifulSoup  # 解析HTML和XML文档

def fetch_movie_list(url):
  # 设置HTTP请求头,模拟了一个运行在Windows 10系统上的Edge浏览器
  # 这样的设置常用于网络爬虫或自动化脚本中,以便让服务器认为请求来自于一个正常的浏览器用户,从而避免被拒绝服务或识别为非标准请求
  headers = {
      'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36'
  }

  # 使用request请求库发送了一个GET请求到指定url,带上了指定的请求头
  response = requests.get(url, headers=headers)
  # http状态码 成功
  if response.status_code == 200:
    soup = BeautifulSoup(response.text, 'html.parser') # 用html.parser解析响应的文本内容
    movie_list = []
    movies = soup.select('#wrapper #content .article .item')
    # 遍历列表movies中的每一个movie对象,将每个电影对象转换成一个格式化过的字符串表示
    all_movies_text = ''.join([movie.prettify() for movie in movies])
    print(all_movies_text)
    return all_movies_text
  else:
    print("Failed to retrieve content")

url = 'https://movie.douban.com/chart'
movies = fetch_movie_list(url)

prompt = f"""
{movies}
这是一段电影列表html,请获取电影名(name),封面链接(picture),简介(info),评分(score),评论人数(commentsNumber),
请使用括号里单词作为属性名,以JSON数组的格式返回
"""
print(prompt)

dashscope.api_key = '自己的密钥'  # 设置API密钥,用于API访问认证

def call_qwen_with_prompt():
  messages = [
      {
          'role': 'user',
          'content': prompt
      }
  ]
  response = dashscope.Generation.call(
      dashscope.Generation.Models.qwen_turbo,
      messages = messages,
      result_messages = 'message'
  )
  print(response)

call_qwen_with_prompt()

执行结果

爬虫:从传统编程到AIGC

补充:js如何拿到user-agent?

let userAgent = navigator.userAgent;

console.log(userAgent);

总结

编程与AIGC

  1. 从以上的代码可以看出,优质的代码应该是模块化的。一个功能的实现超过10行,就应当划分为几个子函数,有利于代码的可读性和复用
  2. 面临一个要解决的问题时,分布细化任务,大问题变小问题
  3. AIGC的核心在于“大模型(LLM)”,为了提高编程效率我们应当思考哪些任务可以被LLM取代。比如大模型擅长文本生成,而在爬虫编程中,解析html文本的工作就可以直接交给大模型处理

JS VS Python

  1. JS是异步的,而Python是同步的。JS是一种事件驱动型语言,可以执行相关的事件处理函数,而不会阻塞页面的其他操作,所以我们也常在JS函数中看见await等关键字;但Python也提供了异步编程库提高性能
  2. js是完全面向对象(原型式面向对象),而python 不是完全面向对象的。比如获取长度,JS写法:movies.length;Python写法:len(movies),在面向对象中的写法中突然多了个函数式编程会让人感到很突兀
转载自:https://juejin.cn/post/7377741132825215012
评论
请登录