实习项目中如何解析Coursera动态加载的法语筛选课程页面?
你的问题核心在于**file_get_html和simple_html_dom只能处理静态HTML内容**,而Coursera的筛选逻辑与课程列表加载是通过前端JavaScript完成的——当你用file_get_html请求URL时,它只会拿到服务器返回的初始页面源码,完全不会执行任何JS代码,所以哪怕你加sleep(10)也没用,因为没有浏览器环境来渲染动态加载的内容。
下面给你两种可行的解决方案,按推荐程度排序:
方案1:直接调用Coursera的API(高效推荐)
Coursera的课程数据是通过AJAX接口加载的,你可以跳过页面渲染环节,直接请求数据接口,这种方式既高效又稳定。步骤如下:
定位API接口:
打开浏览器开发者工具(F12),切换到「Network」标签,筛选「XHR/fetch」类型的请求。然后在Coursera页面上选择法语筛选条件,观察新出现的请求——你会看到一个类似https://www.coursera.org/api/search/index的接口,它的响应是JSON格式的结构化课程数据。用PHP请求API并解析:
复制这个API的完整请求URL,同时带上必要的请求头(比如User-Agent、Accept等,避免被服务器拦截),然后用PHP的curl或者file_get_contents发起请求,最后解析返回的JSON即可。
示例代码:
<?php // 替换为你找到的真实API URL $apiUrl = 'https://www.coursera.org/api/search/index?params=...'; // 设置请求头,模拟真实浏览器请求 $opts = [ 'http' => [ 'method' => 'GET', 'header' => 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36' . "\r\n" . 'Accept: application/json' . "\r\n" ] ]; $context = stream_context_create($opts); // 发起API请求 $response = file_get_contents($apiUrl, false, $context); if ($response === false) { die('API请求失败,请检查URL和请求头'); } // 解析JSON数据 $courses = json_decode($response, true); // 遍历并输出课程信息(具体字段需根据API响应结构调整) foreach ($courses['results'] as $course) { echo "<div class='card-info'>"; echo "<h3>" . htmlspecialchars($course['name']) . "</h3>"; echo "<p>机构:" . htmlspecialchars($course['partnerName']) . "</p>"; echo "</div><br>"; } ?>
方案2:使用无头浏览器模拟页面渲染
如果API接口结构复杂或容易变化,可以用支持JavaScript渲染的无头浏览器工具,比如Symfony Panther(基于ChromeDriver),它能模拟真实浏览器加载页面、执行JS,等待动态内容加载完成后再获取完整HTML。
安装Symfony Panther:
通过Composer安装依赖:composer require symfony/panther编写渲染与解析代码:
下面的代码会模拟浏览器打开目标页面,等待筛选后的课程卡片加载完成,再提取HTML内容进行解析:
<?php require __DIR__.'/vendor/autoload.php'; use Symfony\Component\Panther\PantherTestCase; // 启动无头浏览器 $client = PantherTestCase::createPantherClient(); // 访问目标页面 $targetUrl = 'https://www.coursera.org/courses?query=indices%5Bprod_all_products_custom_ranking_revenuelast28d%5D%5BrefinementList%5D%5Blanguage%5D%5B0%5D=French&indices%5Bprod_all_products_custom_ranking_revenuelast28d%5D%5Bpage%5D=1&indices%5Bprod_all_products_custom_ranking_revenuelast28d%5D%5Bconfigure%5D%5BclickAnalytics%5D=true&indices%5Bprod_all_products_custom_ranking_revenuelast28d%5D%5Bconfigure%5D%5BhitsPerPage%5D=10&configure%5BclickAnalytics%5D=true&page=1'; $client->request('GET', $targetUrl); // 等待课程卡片元素加载完成(确保动态内容渲染完毕) $client->waitFor('.card-info'); // 获取渲染后的完整HTML $html = $client->getCrawler()->html(); // 用simple_html_dom解析HTML(也可直接用Panther的Crawler处理) require_once('simple_html_dom.php'); $dom = str_get_html($html); foreach($dom->find('div.card-info') as $e) { echo $e->outertext . '<br>'; } // 关闭浏览器实例 $client->quit(); ?>
补充:为什么sleep(10)无效?
再明确一下:file_get_html是直接从服务器拉取静态HTML文件,整个过程没有浏览器参与,不会执行页面中的任何JavaScript代码。页面上的筛选逻辑、动态内容加载都是JS在浏览器环境中完成的,所以sleep只是让PHP脚本暂停执行,对已经拿到的静态HTML没有任何影响。
内容的提问来源于stack exchange,提问作者toe

