|
凌晨两点,取网我盯着屏幕上那个返回500错误的页数接口,差点把键盘摔了。取网对方网站改版了,页数登录机制换成了动态token,取网之前写的页数PHP脚本全军覆没。这就是取网抓网页数据的日常——你以为写的是代码,其实是页数在跟对方的前端工程师玩猫鼠游戏。 PHP做爬虫,取网说穿了就是页数三件事:拿HTML、解析HTML、取网存数据。页数没有Python那些Scrapy、取网BeautifulSoup的页数花哨玩意儿,但有curl和正则表达式就够了。取网我最早抓数据用的是最笨的办法——file_get_contents()一把梭,后来发现目标网站加了User-Agent检测,直接给我返回403。换成curl,带上浏览器头,问题就解决了。 $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'http://example.com'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'); $html = curl_exec($ch); curl_close($ch); 这段代码我闭着眼都能写出来。但真正让人头疼的不是拿数据,是解析。PHP的DOMDocument配XPath,比正则好用得多。正则匹配HTML就像用菜刀削苹果——能削,但容易削到手。比如抓商品价格,用正则写/<span class="price">(.*?)<\/span>/,碰上属性顺序变了就完蛋。换成XPath: $dom = new DOMDocument(); @$dom->loadHTML($html); $xpath = new DOMXPath($dom); $price = $xpath->query('//span[contains(@class, "price")]')->item(0)->nodeValue; 稳得很。 但光会抓静态页面没用。现在稍微像样点的网站,数据都是JS动态渲染的。PHP抓到的只是一堆空壳div。这时候得用headless browser,PHP里可以用Facebook的WebDriver或者PhantomJS(虽然它已经死了)。我上次抓一个旅游网站,列表页是动态加载的,翻页按钮点下去URL不变。折腾半天,最后用WebDriver模拟点击,每次等两秒让数据渲染完,再抓DOM。慢是慢点,但能用。 还有个坑——反爬虫。有些网站会检测请求频率,你一秒发十个请求,IP就被封了。我吃过亏,一个电商网站抓了两千条商品数据,突然断线,再访问就跳验证码页面。后来学乖了,抓取间隔随机设成2到5秒,每次请求前先GET一下robots.txt看人家允不允许。别觉得这多余,有些法律风险真不是开玩笑的。我认识一个哥们,抓人家数据卖钱,被索赔二十万。 存储这块,别用MySQL直接插。数据量大了,连接池撑不住。我习惯先抓完存CSV,完事了再导入数据库。或者用Redis做队列,多进程并发抓。PHP的pcntl_fork能开子进程,每个进程负责不同页面,效率能提三倍。不过小心内存泄漏,抓完一个页面记得unset掉大变量。 写到最后发现,抓网页数据这事儿,技术只占一半,另一半是耐心。今天能跑的代码,明天对方网站改个class名就废了。所以代码里多写注释,把选择器路径记清楚,下次改的时候不至于骂娘。还有,抓下来的数据别直接用,清洗一下——去空格、转编码、过滤HTML标签,不然存进数据库的全是垃圾。 PHP抓数据不优雅,但胜在部署简单,一台破服务器就能跑。Python要装一堆依赖,PHP开箱即用。真遇到高并发抓取,PHP也能配合Swoole做协程。别被网上那些"PHP已死"的论调忽悠,工具顺手就行。我靠着这套土办法,抓了三年数据,养活了自己。 |