爬虫:从传统编程到AIGC
引言
随着人工智能技术的飞跃式发展,特别是生成式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环境下)
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,拿到后复制后续要用
二、安装第三方包
本例的编程平台是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()
执行结果
补充:js如何拿到user-agent?
let userAgent = navigator.userAgent;
console.log(userAgent);
总结
编程与AIGC
- 从以上的代码可以看出,优质的代码应该是模块化的。一个功能的实现超过10行,就应当划分为几个子函数,有利于代码的可读性和复用
- 面临一个要解决的问题时,分布细化任务,大问题变小问题
- AIGC的核心在于“大模型(LLM)”,为了提高编程效率我们应当思考哪些任务可以被LLM取代。比如大模型擅长文本生成,而在爬虫编程中,解析html文本的工作就可以直接交给大模型处理
JS VS Python
- JS是异步的,而Python是同步的。JS是一种事件驱动型语言,可以执行相关的事件处理函数,而不会阻塞页面的其他操作,所以我们也常在JS函数中看见await等关键字;但Python也提供了异步编程库提高性能
- js是完全面向对象(原型式面向对象),而python 不是完全面向对象的。比如获取长度,JS写法:movies.length;Python写法:len(movies),在面向对象中的写法中突然多了个函数式编程会让人感到很突兀
转载自:https://juejin.cn/post/7377741132825215012