<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Factden</title>
    <description>The latest articles on DEV Community by Factden (@factden).</description>
    <link>https://dev.to/factden</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3962014%2Ff955bba2-cff2-46e4-af7d-48e34fb307d4.png</url>
      <title>DEV Community: Factden</title>
      <link>https://dev.to/factden</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/factden"/>
    <language>en</language>
    <item>
      <title>How to Scrape Hotels.com Reviews in 2026 (Python + a No-Code Shortcut)</title>
      <dc:creator>Factden</dc:creator>
      <pubDate>Tue, 14 Jul 2026 08:04:09 +0000</pubDate>
      <link>https://dev.to/factden/how-to-scrape-hotelscom-reviews-in-2026-python-a-no-code-shortcut-3p7a</link>
      <guid>https://dev.to/factden/how-to-scrape-hotelscom-reviews-in-2026-python-a-no-code-shortcut-3p7a</guid>
      <description>&lt;p&gt;&lt;a href="https://www.hotels.com" rel="noopener noreferrer"&gt;Hotels.com&lt;/a&gt; is part of Expedia Group, which is good news and a small headache. Good news: its reviews share the same backend as Expedia, Travelocity, Orbitz, and the rest, so one approach covers all of them. The headache: Hotels.com still uses its old &lt;code&gt;/ho&amp;lt;id&amp;gt;/&lt;/code&gt; URL format, which has to be resolved to the global property before you can pull anything.&lt;/p&gt;

&lt;p&gt;This guide covers what you can pull from a Hotels.com review, why the DIY route is fiddly, working Python, a no-code shortcut, and a plain comparison. If you want the deeper reference, there is a full &lt;a href="https://factden.com/blog/how-to-scrape-hotels-com-reviews" rel="noopener noreferrer"&gt;guide to scraping Hotels.com reviews&lt;/a&gt; too.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can pull from a review
&lt;/h2&gt;

&lt;p&gt;Per review you can get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Overall rating on the /10 scale&lt;/strong&gt; plus a label, and category &lt;strong&gt;sub-ratings&lt;/strong&gt;: Cleanliness, Service, Room comfort, Hotel condition, Amenities, Eco-friendliness&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The full review text&lt;/strong&gt;, detected language, and a translation flag&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stay context&lt;/strong&gt;: check-in and check-out dates, travel companions, traveler category&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reviewer detail&lt;/strong&gt;, verified flag, helpful votes, review photos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Owner responses&lt;/strong&gt;, and a &lt;code&gt;brandType&lt;/code&gt; field&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An LLM-ready markdown block&lt;/strong&gt; per review&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Same clean, structured shape whether the review came from Hotels.com or any sibling brand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why scraping Hotels.com is hard
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No public reviews API and no API key on offer.&lt;/strong&gt; Scraping the public pages is the route.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;/ho&amp;lt;id&amp;gt;/&lt;/code&gt; legacy id.&lt;/strong&gt; A Hotels.com URL like &lt;code&gt;hotels.com/ho119566/&lt;/code&gt; does not carry the global property id directly; it has to be resolved first, or your request goes nowhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reviews load dynamically&lt;/strong&gt;, so a plain request returns a JavaScript shell, not the data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bot protection and rate limits&lt;/strong&gt; on top.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So the work is resolving the legacy id, hitting the review endpoints, and staying unblocked, on repeat.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three ways to get the data
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;DIY Python&lt;/th&gt;
&lt;th&gt;Hotels.com Reviews Scraper (actor)&lt;/th&gt;
&lt;th&gt;Official API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Setup time&lt;/td&gt;
&lt;td&gt;Hours to days&lt;/td&gt;
&lt;td&gt;~30 seconds&lt;/td&gt;
&lt;td&gt;Not available (no API key)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;/ho&amp;lt;id&amp;gt;/&lt;/code&gt; resolution&lt;/td&gt;
&lt;td&gt;You build it&lt;/td&gt;
&lt;td&gt;Automatic&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/10 ratings + 6 sub-ratings&lt;/td&gt;
&lt;td&gt;Parse nested markup&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Owner responses&lt;/td&gt;
&lt;td&gt;Extra parsing&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;Proxies + eng time&lt;/td&gt;
&lt;td&gt;Pay-per-result&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;One-off&lt;/td&gt;
&lt;td&gt;Scheduled, at scale&lt;/td&gt;
&lt;td&gt;Not an option&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Option A: DIY in Python
&lt;/h2&gt;

&lt;p&gt;A plain request to a Hotels.com page returns a shell, and the &lt;code&gt;/ho&amp;lt;id&amp;gt;/&lt;/code&gt; id is not the one the review endpoint expects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.hotels.com/ho119566/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User-Agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mozilla/5.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reviews&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# usually False; ho-id must be resolved, reviews load separately
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get the data you resolve the &lt;code&gt;/ho&amp;lt;id&amp;gt;/&lt;/code&gt; to the global property id, call the review endpoints, and normalize the response. Fine for one hotel, several moving parts for a portfolio.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option B: the no-code / API shortcut
&lt;/h2&gt;

&lt;p&gt;When you want clean rows, the &lt;a href="https://apify.com/factden/hotels-com-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Hotels.com Reviews Scraper&lt;/a&gt; on Apify resolves the &lt;code&gt;/ho&amp;lt;id&amp;gt;/&lt;/code&gt; id for you and returns structured JSON. No login, no API key, no proxy setup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ApifyClient&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;YOUR_APIFY_TOKEN&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;factden/hotels-com-reviews-scraper&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hotelUrls&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.hotels.com/ho119566/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;242128&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxReviewsPerHotel&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sortBy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;newest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;overallRating&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reviewText&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][:&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pass Hotels.com &lt;code&gt;/ho&amp;lt;id&amp;gt;/&lt;/code&gt; URLs or bare ids, and any other Expedia Group brand URL works too. &lt;code&gt;sortBy&lt;/code&gt; takes &lt;code&gt;newest&lt;/code&gt;, &lt;code&gt;oldest&lt;/code&gt;, &lt;code&gt;highestRating&lt;/code&gt;, or &lt;code&gt;lowestRating&lt;/code&gt;; bound results with &lt;code&gt;fromDate&lt;/code&gt; / &lt;code&gt;toDate&lt;/code&gt; and &lt;code&gt;minRating&lt;/code&gt; / &lt;code&gt;maxRating&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes back: 27 fields per review
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Group&lt;/th&gt;
&lt;th&gt;Fields&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Core&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;reviewId&lt;/code&gt;, &lt;code&gt;hotelId&lt;/code&gt;, &lt;code&gt;hotelName&lt;/code&gt;, &lt;code&gt;hotelUrl&lt;/code&gt;, &lt;code&gt;source&lt;/code&gt;, &lt;code&gt;brandType&lt;/code&gt;, &lt;code&gt;submittedAt&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rating&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;overallRating&lt;/code&gt; (/10), &lt;code&gt;ratingLabel&lt;/code&gt;, &lt;code&gt;subRatings&lt;/code&gt; (cleanliness, service, room comfort, condition, amenities, eco)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Text&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;reviewText&lt;/code&gt;, &lt;code&gt;language&lt;/code&gt;, &lt;code&gt;isMachineTranslated&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stay&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;checkInDate&lt;/code&gt;, &lt;code&gt;checkOutDate&lt;/code&gt;, &lt;code&gt;travelCompanions&lt;/code&gt;, &lt;code&gt;travelerCategories&lt;/code&gt;, &lt;code&gt;roomTypeId&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reviewer&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;reviewerName&lt;/code&gt;, &lt;code&gt;reviewerLocation&lt;/code&gt;, &lt;code&gt;verified&lt;/code&gt;, &lt;code&gt;isAnonymous&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signals&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;helpfulVotes&lt;/code&gt;, &lt;code&gt;imagesCount&lt;/code&gt;, &lt;code&gt;reviewPhotos&lt;/code&gt;, &lt;code&gt;ownerResponse&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI-ready&lt;/td&gt;
&lt;td&gt;&lt;code&gt;markdownContent&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A trimmed sample row:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"brandType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hotels.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hotelName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The Venetian Resort"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"overallRating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ratingLabel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Wonderful"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"subRatings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Cleanliness: 10"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Service: 9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hotel condition: 9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Amenities: 10"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reviewText"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Great suite, easy check-in, would stay again."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"travelerCategories"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Couple"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"checkInDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-18"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"checkOutDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-21"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"language"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"helpfulVotes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full field list and snippets are in the &lt;a href="https://github.com/factden/hotels-com-reviews-scraper" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grab a free sample dataset
&lt;/h2&gt;

&lt;p&gt;Want to see the data first? There is a free Hotels.com sample (CSV/JSON) here: &lt;strong&gt;&lt;a href="https://factden.com/sample-hotels" rel="noopener noreferrer"&gt;factden.com/sample-hotels&lt;/a&gt;&lt;/strong&gt;. Load it into pandas and the /10 ratings and sub-ratings are ready to plot.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is scraping Hotels.com legal?&lt;/strong&gt; The reviews are publicly visible. As with any scraping, check Hotels.com's Terms of Service and your local rules, and use the data responsibly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is there a Hotels.com API?&lt;/strong&gt; No public reviews API and no key on offer. Scraping the public pages is the route.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the &lt;code&gt;/ho&amp;lt;id&amp;gt;/&lt;/code&gt; thing?&lt;/strong&gt; Hotels.com uses a legacy property id in its URLs that does not match the global Expedia Group id. It has to be resolved first, which the actor does automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I scrape Expedia and the other brands too?&lt;/strong&gt; Yes. Hotels.com shares the Expedia Group backend, so the same actor handles Expedia, Travelocity, Orbitz, Wotif, CheapTickets, and ebookers, same schema, tagged by &lt;code&gt;brandType&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I stop getting blocked?&lt;/strong&gt; Residential proxies and slow pacing, or a tool that resolves the id and reads the review endpoints directly. Plain requests get a JavaScript shell.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Doing B2B software instead of hotels? See &lt;a href="https://dev.to/factden/how-to-scrape-g2-reviews-in-2026-python-a-no-code-shortcut-18eb"&gt;how to scrape G2 reviews&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Other FactDen scrapers: &lt;a href="https://apify.com/factden/expedia-hotel-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Expedia hotel reviews&lt;/a&gt; and &lt;a href="https://apify.com/factden/ctrip-trip-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Trip.com and Ctrip hotel reviews&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Questions, or a field you wish it extracted? Drop a comment.&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>python</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Scrape Expedia Hotel Reviews in 2026 (Python + a No-Code Shortcut)</title>
      <dc:creator>Factden</dc:creator>
      <pubDate>Tue, 14 Jul 2026 08:02:08 +0000</pubDate>
      <link>https://dev.to/factden/how-to-scrape-expedia-hotel-reviews-in-2026-python-a-no-code-shortcut-3m9o</link>
      <guid>https://dev.to/factden/how-to-scrape-expedia-hotel-reviews-in-2026-python-a-no-code-shortcut-3m9o</guid>
      <description>&lt;p&gt;Here is the thing most people miss about scraping &lt;a href="https://www.expedia.com" rel="noopener noreferrer"&gt;Expedia&lt;/a&gt;: Expedia, Hotels.com, Travelocity, Orbitz, Wotif, CheapTickets, and ebookers all run on the &lt;strong&gt;same backend&lt;/strong&gt;. The same hotel shows up across all seven brands, and their reviews pool together. If you scrape only &lt;code&gt;expedia.com&lt;/code&gt;, you leave most of the data on the table.&lt;/p&gt;

&lt;p&gt;This guide covers what you can pull from an Expedia Group review, why the DIY route is fiddly, working Python, a no-code shortcut, and a plain comparison. If you want the deeper reference, there is a full &lt;a href="https://factden.com/blog/how-to-scrape-expedia-reviews" rel="noopener noreferrer"&gt;guide to scraping Expedia reviews&lt;/a&gt; too.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can pull from a review
&lt;/h2&gt;

&lt;p&gt;Per review you can get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Overall rating on the /10 scale&lt;/strong&gt; plus a label, and category &lt;strong&gt;sub-ratings&lt;/strong&gt;: Cleanliness, Service, Room comfort, Hotel condition, Amenities, Eco-friendliness&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The full review text&lt;/strong&gt;, detected language, and a translation flag&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stay context&lt;/strong&gt;: check-in and check-out dates, travel companions, traveler category&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reviewer detail&lt;/strong&gt;, verified flag, helpful votes, review photos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Owner responses&lt;/strong&gt;, and a &lt;code&gt;brandType&lt;/code&gt; telling you which of the seven brands the review came from&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An LLM-ready markdown block&lt;/strong&gt; per review&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;brandType&lt;/code&gt; field plus the shared backend are what let you pull every brand's reviews for a property in one pass.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why scraping Expedia is hard
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;There is no public reviews API.&lt;/strong&gt; Expedia Group does not offer one, so scraping the public pages is the route.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seven brands, different URL formats.&lt;/strong&gt; Six brands embed the global property id in a &lt;code&gt;.h&amp;lt;id&amp;gt;.&lt;/code&gt; path; Hotels.com uses a legacy &lt;code&gt;/ho&amp;lt;id&amp;gt;/&lt;/code&gt; id that has to be resolved. A scraper built for one brand misses the rest.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reviews are loaded dynamically&lt;/strong&gt;, not sitting in the initial HTML, so a plain request gets you a shell.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bot protection and rate limits&lt;/strong&gt; on top.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So the work is resolving ids across brands, hitting the review endpoints, and staying unblocked, on repeat.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three ways to get the data
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;DIY Python&lt;/th&gt;
&lt;th&gt;Expedia Reviews Scraper (actor)&lt;/th&gt;
&lt;th&gt;Official API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Setup time&lt;/td&gt;
&lt;td&gt;Hours to days&lt;/td&gt;
&lt;td&gt;~30 seconds&lt;/td&gt;
&lt;td&gt;Not available (no public reviews API)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;All 7 Expedia Group brands&lt;/td&gt;
&lt;td&gt;Build a scraper per brand&lt;/td&gt;
&lt;td&gt;One run&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/10 ratings + 6 sub-ratings&lt;/td&gt;
&lt;td&gt;Parse nested markup&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Owner responses&lt;/td&gt;
&lt;td&gt;Extra parsing&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;Proxies + eng time&lt;/td&gt;
&lt;td&gt;Pay-per-result&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;One-off&lt;/td&gt;
&lt;td&gt;Scheduled, at scale&lt;/td&gt;
&lt;td&gt;Not an option&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Option A: DIY in Python
&lt;/h2&gt;

&lt;p&gt;A plain request to a hotel page returns a JavaScript shell, not the reviews:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.expedia.com/Las-Vegas-Hotels-Bellagio.h140596.Hotel-Information&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User-Agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mozilla/5.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reviews&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# usually False; reviews load via internal endpoints
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get the data you reverse-engineer the review endpoints, map each brand's URL format to the global property id (including the Hotels.com &lt;code&gt;/ho&amp;lt;id&amp;gt;/&lt;/code&gt; resolution), and normalize the responses. Fine for one hotel, a lot of moving parts for a portfolio.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option B: the no-code / API shortcut
&lt;/h2&gt;

&lt;p&gt;When you want clean rows, the &lt;a href="https://apify.com/factden/expedia-hotel-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Expedia Reviews Scraper&lt;/a&gt; on Apify takes any Expedia Group hotel URL (or a bare property id), resolves it, and returns structured JSON. No login, no proxy setup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ApifyClient&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;YOUR_APIFY_TOKEN&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;factden/expedia-hotel-reviews-scraper&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hotelUrls&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.expedia.com/Las-Vegas-Hotels-Bellagio.h140596.Hotel-Information&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;140596&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.hotels.com/ho115902/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxReviewsPerHotel&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sortBy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;newest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;brandType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;overallRating&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reviewText&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][:&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mix brands and bare ids freely. &lt;code&gt;sortBy&lt;/code&gt; takes &lt;code&gt;newest&lt;/code&gt;, &lt;code&gt;oldest&lt;/code&gt;, &lt;code&gt;highestRating&lt;/code&gt;, or &lt;code&gt;lowestRating&lt;/code&gt;; bound results with &lt;code&gt;fromDate&lt;/code&gt; / &lt;code&gt;toDate&lt;/code&gt; and &lt;code&gt;minRating&lt;/code&gt; / &lt;code&gt;maxRating&lt;/code&gt;; and use &lt;code&gt;reviewSources&lt;/code&gt; to keep only certain brands' reviews.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes back: 27 fields per review
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Group&lt;/th&gt;
&lt;th&gt;Fields&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Core&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;reviewId&lt;/code&gt;, &lt;code&gt;hotelId&lt;/code&gt;, &lt;code&gt;hotelName&lt;/code&gt;, &lt;code&gt;hotelUrl&lt;/code&gt;, &lt;code&gt;source&lt;/code&gt;, &lt;code&gt;brandType&lt;/code&gt;, &lt;code&gt;submittedAt&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rating&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;overallRating&lt;/code&gt; (/10), &lt;code&gt;ratingLabel&lt;/code&gt;, &lt;code&gt;subRatings&lt;/code&gt; (cleanliness, service, room comfort, condition, amenities, eco)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Text&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;reviewText&lt;/code&gt;, &lt;code&gt;language&lt;/code&gt;, &lt;code&gt;isMachineTranslated&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stay&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;checkInDate&lt;/code&gt;, &lt;code&gt;checkOutDate&lt;/code&gt;, &lt;code&gt;travelCompanions&lt;/code&gt;, &lt;code&gt;travelerCategories&lt;/code&gt;, &lt;code&gt;roomTypeId&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reviewer&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;reviewerName&lt;/code&gt;, &lt;code&gt;reviewerLocation&lt;/code&gt;, &lt;code&gt;verified&lt;/code&gt;, &lt;code&gt;isAnonymous&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signals&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;helpfulVotes&lt;/code&gt;, &lt;code&gt;imagesCount&lt;/code&gt;, &lt;code&gt;reviewPhotos&lt;/code&gt;, &lt;code&gt;ownerResponse&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI-ready&lt;/td&gt;
&lt;td&gt;&lt;code&gt;markdownContent&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A trimmed sample row:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"brandType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Expedia"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hotelName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bellagio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"overallRating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ratingLabel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Exceptional"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"subRatings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Cleanliness: 10"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Service: 10"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hotel condition: 10"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Amenities: 10"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reviewText"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Rooms were great. Buffet was awesome. Nice casino."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"travelerCategories"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Friends"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"checkInDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-05-01"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"checkOutDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-05-04"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"language"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"helpfulVotes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full field list and snippets are in the &lt;a href="https://github.com/factden/expedia-hotel-reviews-scraper" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grab a free sample dataset
&lt;/h2&gt;

&lt;p&gt;Want to see the data first? There is a free Expedia sample (CSV/JSON) here: &lt;strong&gt;&lt;a href="https://factden.com/sample-expedia" rel="noopener noreferrer"&gt;factden.com/sample-expedia&lt;/a&gt;&lt;/strong&gt;. Load it into pandas and the /10 ratings and sub-ratings are ready to plot.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is scraping Expedia legal?&lt;/strong&gt; The reviews are publicly visible. As with any scraping, check Expedia's Terms of Service and your local rules, and use the data responsibly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is there an official Expedia reviews API?&lt;/strong&gt; No public one. Scraping the public pages is the route for most teams.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I scrape Hotels.com and the other brands too?&lt;/strong&gt; Yes. All seven Expedia Group brands share the backend, so one actor covers Expedia, Hotels.com, Travelocity, Orbitz, Wotif, CheapTickets, and ebookers, with the same schema tagged by &lt;code&gt;brandType&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Are ratings on the 5-star or 10-point scale?&lt;/strong&gt; Expedia Group uses /10, and that is what the actor returns for &lt;code&gt;overallRating&lt;/code&gt; and the sub-ratings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I stop getting blocked?&lt;/strong&gt; Residential proxies and slow pacing, or a tool that reads the review endpoints directly. Plain requests get a JavaScript shell, not the data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Doing B2B software instead of hotels? See &lt;a href="https://dev.to/factden/how-to-scrape-g2-reviews-in-2026-python-a-no-code-shortcut-18eb"&gt;how to scrape G2 reviews&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Other FactDen scrapers: &lt;a href="https://apify.com/factden/hotels-com-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Hotels.com reviews&lt;/a&gt; and &lt;a href="https://apify.com/factden/ctrip-trip-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Trip.com and Ctrip hotel reviews&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Questions, or a field you wish it extracted? Drop a comment.&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>python</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Scrape Trip.com and Ctrip Hotel Reviews in 2026 (Python + a No-Code Shortcut)</title>
      <dc:creator>Factden</dc:creator>
      <pubDate>Tue, 14 Jul 2026 08:00:07 +0000</pubDate>
      <link>https://dev.to/factden/how-to-scrape-tripcom-and-ctrip-hotel-reviews-in-2026-python-a-no-code-shortcut-in6</link>
      <guid>https://dev.to/factden/how-to-scrape-tripcom-and-ctrip-hotel-reviews-in-2026-python-a-no-code-shortcut-in6</guid>
      <description>&lt;p&gt;Most "Trip.com scrapers" only see half the picture. &lt;a href="https://www.trip.com" rel="noopener noreferrer"&gt;Trip.com&lt;/a&gt; is the international brand, but the same hotel usually has a much larger pool of reviews on its Chinese-domestic sibling, &lt;code&gt;hotels.ctrip.com&lt;/code&gt; (携程). If you want honest guest sentiment for a property in Asia, you need both, and you need the Chinese ones translated.&lt;/p&gt;

&lt;p&gt;This guide covers what you can pull from a Trip.com or Ctrip review, why the DIY route is a two-headed problem, working Python, a no-code shortcut, and a plain comparison. If you want the deeper reference, there is a full &lt;a href="https://factden.com/blog/how-to-scrape-ctrip-trip-reviews" rel="noopener noreferrer"&gt;guide to scraping Trip.com and Ctrip reviews&lt;/a&gt; too.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can pull from a review
&lt;/h2&gt;

&lt;p&gt;Per review you can get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Overall rating&lt;/strong&gt; and a label ("Outstanding"), plus &lt;strong&gt;sub-ratings&lt;/strong&gt;: Cleanliness, Location, Service, Facilities&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The review text, plus an English translation&lt;/strong&gt; of Chinese reviews (&lt;code&gt;reviewTextTranslated&lt;/code&gt;), and the detected &lt;code&gt;language&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trip context&lt;/strong&gt;: travel type (family, couple, business, solo), room name, check-in month&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reviewer detail&lt;/strong&gt;: tier ("Review star"), lifetime review count, and for Ctrip the reviewer's Chinese province&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Owner responses&lt;/strong&gt; (text and date), useful counts, photo/video flags&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An LLM-ready markdown block&lt;/strong&gt; per review&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The two things that separate a real dataset from a shallow one: the &lt;strong&gt;Chinese-domestic feed&lt;/strong&gt; and its &lt;strong&gt;translation&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why scraping Trip.com and Ctrip is hard
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;There is no public reviews API.&lt;/strong&gt; Trip.com Group does not offer one, so scraping the public pages is the only route.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It is two systems, not one.&lt;/strong&gt; &lt;code&gt;trip.com&lt;/code&gt; (international) and &lt;code&gt;hotels.ctrip.com&lt;/code&gt; / 携程 (Chinese) have different structures and languages. A scraper built for one usually misses the other, and the Chinese pool is often the larger one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chinese reviews need translation&lt;/strong&gt; to be usable in an English pipeline, which is a whole extra step.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bot protection and rate limits&lt;/strong&gt; apply on both.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So the work is not parsing one page. It is handling both locales, translating, and staying unblocked, on repeat.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three ways to get the data
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;DIY Python&lt;/th&gt;
&lt;th&gt;Trip.com &amp;amp; Ctrip actor&lt;/th&gt;
&lt;th&gt;Official API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Setup time&lt;/td&gt;
&lt;td&gt;Hours to days&lt;/td&gt;
&lt;td&gt;~30 seconds&lt;/td&gt;
&lt;td&gt;Not available (no public reviews API)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Both Trip.com + Ctrip feeds&lt;/td&gt;
&lt;td&gt;Build two scrapers&lt;/td&gt;
&lt;td&gt;One run&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chinese review translation&lt;/td&gt;
&lt;td&gt;Add a translation step&lt;/td&gt;
&lt;td&gt;Built in&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sub-ratings + owner responses&lt;/td&gt;
&lt;td&gt;Parse nested markup&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;Proxies + eng time&lt;/td&gt;
&lt;td&gt;Pay-per-result&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;One-off&lt;/td&gt;
&lt;td&gt;Scheduled, at scale&lt;/td&gt;
&lt;td&gt;Not an option&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Option A: DIY in Python
&lt;/h2&gt;

&lt;p&gt;A plain request to a hotel page tends to come back blocked or JavaScript-only:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.trip.com/hotels/macau-hotel-detail-344983/galaxy-hotel/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User-Agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mozilla/5.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# challenge / JS-rendered shell, reviews not in the HTML
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reviews load through internal endpoints, so you end up reverse-engineering those (per locale), then normalizing two different response shapes, then translating the Chinese text. Doable for one hotel, painful for a portfolio on a schedule.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option B: the no-code / API shortcut
&lt;/h2&gt;

&lt;p&gt;When you want clean rows, the &lt;a href="https://apify.com/factden/ctrip-trip-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Trip.com &amp;amp; Ctrip Reviews Scraper&lt;/a&gt; on Apify reads &lt;strong&gt;both feeds in a single run&lt;/strong&gt; and hands back structured JSON with the Chinese reviews already translated. No login, no proxy setup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ApifyClient&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;YOUR_APIFY_TOKEN&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;factden/ctrip-trip-reviews-scraper&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;startUrls&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.trip.com/hotels/macau-hotel-detail-344983/galaxy-hotel/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://hotels.ctrip.com/hotels/1286148.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxReviewsPerHotel&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sortBy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mostRecent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;overallRating&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;travelType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mix &lt;code&gt;trip.com&lt;/code&gt; and &lt;code&gt;hotels.ctrip.com&lt;/code&gt; URLs freely. &lt;code&gt;sortBy&lt;/code&gt; takes &lt;code&gt;mostRelevant&lt;/code&gt;, &lt;code&gt;mostRecent&lt;/code&gt;, &lt;code&gt;ratingHighToLow&lt;/code&gt;, or &lt;code&gt;ratingLowToHigh&lt;/code&gt;, and you can bound results with &lt;code&gt;fromDate&lt;/code&gt; / &lt;code&gt;toDate&lt;/code&gt; and &lt;code&gt;minRating&lt;/code&gt; / &lt;code&gt;maxRating&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes back: 23 fields per review
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Group&lt;/th&gt;
&lt;th&gt;Fields&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Core&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;reviewId&lt;/code&gt;, &lt;code&gt;hotelId&lt;/code&gt;, &lt;code&gt;hotelName&lt;/code&gt;, &lt;code&gt;hotelUrl&lt;/code&gt;, &lt;code&gt;source&lt;/code&gt; (trip.com / ctrip), &lt;code&gt;submittedAt&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rating&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;overallRating&lt;/code&gt;, &lt;code&gt;ratingLabel&lt;/code&gt;, &lt;code&gt;subRatings&lt;/code&gt; (Cleanliness, Location, Service, Facilities)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Text&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;reviewText&lt;/code&gt;, &lt;code&gt;reviewTextTranslated&lt;/code&gt;, &lt;code&gt;isMachineTranslated&lt;/code&gt;, &lt;code&gt;language&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reviewer&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;reviewer&lt;/code&gt; (name, tier, lifetime reviews, Chinese province for Ctrip)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trip context&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;travelType&lt;/code&gt;, &lt;code&gt;roomName&lt;/code&gt;, &lt;code&gt;checkInMonth&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signals&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;recommends&lt;/code&gt;, &lt;code&gt;usefulCount&lt;/code&gt;, &lt;code&gt;imagesCount&lt;/code&gt;, &lt;code&gt;hasVideo&lt;/code&gt;, &lt;code&gt;ownerResponse&lt;/code&gt; (text, date)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI-ready&lt;/td&gt;
&lt;td&gt;&lt;code&gt;markdownContent&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A trimmed sample row (a Chinese review, translated):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ctrip"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hotelName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Galaxy Hotel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"overallRating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ratingLabel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Outstanding"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"subRatings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Cleanliness: 5.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Location: 5.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Service: 5.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Facilities: 5.0"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"language"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"zh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reviewText"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"酒店很棒，服务一流，位置方便。"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reviewTextTranslated"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Great hotel, first-class service, convenient location."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"isMachineTranslated"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"travelType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Family"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reviewer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"tier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Rising review star"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"lifetimeReviews"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ownerResponse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"感謝您的正面評價..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-06-15"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full field list and snippets are in the &lt;a href="https://github.com/factden/ctrip-trip-reviews-scraper" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grab a free sample dataset
&lt;/h2&gt;

&lt;p&gt;Want to see the data first? There is a free Trip.com/Ctrip sample (CSV/JSON) here: &lt;strong&gt;&lt;a href="https://factden.com/sample-trip" rel="noopener noreferrer"&gt;factden.com/sample-trip&lt;/a&gt;&lt;/strong&gt;. It includes both English and translated-Chinese reviews so you can see the shape.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is scraping Trip.com or Ctrip legal?&lt;/strong&gt; The reviews are publicly visible. As with any scraping, check the sites' Terms of Service and your local rules, and use the data responsibly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is there an official Trip.com / Ctrip reviews API?&lt;/strong&gt; No public one. Scraping the public pages is the only route for most teams.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I get the Chinese reviews in English?&lt;/strong&gt; Yes. Ctrip reviews come with &lt;code&gt;reviewTextTranslated&lt;/code&gt; and an &lt;code&gt;isMachineTranslated&lt;/code&gt; flag, alongside the original.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I scrape both Trip.com and Ctrip at once?&lt;/strong&gt; Yes, that is the point. Mix both URL types in one run and the output uses the same schema, tagged by &lt;code&gt;source&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I stop getting blocked?&lt;/strong&gt; Residential proxies and slow pacing, or a tool that reads the internal endpoints per locale. Plain requests will get challenged.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Doing B2B software instead of hotels? See &lt;a href="https://dev.to/factden/how-to-scrape-g2-reviews-in-2026-python-a-no-code-shortcut-18eb"&gt;how to scrape G2 reviews&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Other FactDen scrapers: &lt;a href="https://apify.com/factden/expedia-hotel-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Expedia hotel reviews&lt;/a&gt; and &lt;a href="https://apify.com/factden/indeed-jobs-scraper?fpr=factden" rel="noopener noreferrer"&gt;Indeed jobs and salary data&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Questions, or a field you wish it extracted? Drop a comment.&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>python</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Scrape Indeed Jobs in 2026 (Python + a No-Code Shortcut)</title>
      <dc:creator>Factden</dc:creator>
      <pubDate>Tue, 14 Jul 2026 07:56:17 +0000</pubDate>
      <link>https://dev.to/factden/how-to-scrape-indeed-jobs-in-2026-python-a-no-code-shortcut-o5j</link>
      <guid>https://dev.to/factden/how-to-scrape-indeed-jobs-in-2026-python-a-no-code-shortcut-o5j</guid>
      <description>&lt;p&gt;Scraping &lt;a href="https://www.indeed.com" rel="noopener noreferrer"&gt;Indeed&lt;/a&gt; sounds simple until you try it. There is no public API anymore, the site sits behind Cloudflare, and the salary you actually want arrives as a messy string like &lt;code&gt;"$120,000 - $160,000 a year"&lt;/code&gt;. This guide covers what you can pull from an Indeed job, why the DIY route is harder than it looks, working Python, a no-code shortcut, and a straight comparison so you can pick a lane. If you want the deeper reference, there is a full &lt;a href="https://factden.com/blog/how-to-scrape-indeed-jobs" rel="noopener noreferrer"&gt;guide to scraping Indeed jobs&lt;/a&gt; too.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can pull from an Indeed job
&lt;/h2&gt;

&lt;p&gt;Per listing you can get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Title, company, and location&lt;/strong&gt; (plus city, state, country code, and lat/long)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Salary, parsed into numbers&lt;/strong&gt;: &lt;code&gt;salaryMin&lt;/code&gt;, &lt;code&gt;salaryMax&lt;/code&gt;, &lt;code&gt;salaryPeriod&lt;/code&gt;, &lt;code&gt;currency&lt;/code&gt;, not just the raw string&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Job type, remote/hybrid flag, and the full description text&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Benefits, date posted, easy-apply and urgent-hire flags&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Company data on demand&lt;/strong&gt;: rating, review count, industry, size, revenue, founded, website, and socials&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Parsed salary and company enrichment are the two things people actually come for, and the two things a naive scrape gets wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why scraping Indeed is hard
&lt;/h2&gt;

&lt;p&gt;Three reasons DIY is more work than it looks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;There is no official API.&lt;/strong&gt; Indeed retired its public job-search API to new developers years ago, so there is no supported programmatic route. Scraping is the only option left.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloudflare and rate limits.&lt;/strong&gt; A plain request to a search results page tends to return a &lt;code&gt;403&lt;/code&gt; or a challenge. Hammer it and you get blocked fast.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Messy salary strings.&lt;/strong&gt; Indeed shows salary as free text. Turning &lt;code&gt;"$120,000 - $160,000 a year"&lt;/code&gt; into clean &lt;code&gt;salaryMin&lt;/code&gt;/&lt;code&gt;salaryMax&lt;/code&gt;/&lt;code&gt;period&lt;/code&gt; reliably, across currencies and 40 countries, is fiddly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So the work is not the parsing of one page. It is staying unblocked, normalizing salary, and pulling company data from separate pages, on repeat.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three ways to get Indeed job data
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;DIY Python&lt;/th&gt;
&lt;th&gt;Indeed Jobs Scraper (actor)&lt;/th&gt;
&lt;th&gt;Indeed Official API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Setup time&lt;/td&gt;
&lt;td&gt;Hours to days&lt;/td&gt;
&lt;td&gt;~30 seconds&lt;/td&gt;
&lt;td&gt;Not available (retired)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Anti-bot handled&lt;/td&gt;
&lt;td&gt;You (Cloudflare, blocks)&lt;/td&gt;
&lt;td&gt;Built in&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Salary as numbers&lt;/td&gt;
&lt;td&gt;You parse the string&lt;/td&gt;
&lt;td&gt;Yes: &lt;code&gt;salaryMin&lt;/code&gt; / &lt;code&gt;salaryMax&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Company data&lt;/td&gt;
&lt;td&gt;Separate scrape&lt;/td&gt;
&lt;td&gt;Free, one toggle&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Countries&lt;/td&gt;
&lt;td&gt;You handle localization&lt;/td&gt;
&lt;td&gt;40+&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;Proxies + eng time&lt;/td&gt;
&lt;td&gt;Pay-per-result&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;One-off search&lt;/td&gt;
&lt;td&gt;Scheduled, at scale&lt;/td&gt;
&lt;td&gt;Not an option&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The official-API column is the real story: since Indeed closed its API, a scraper (yours or a ready-made one) is the only way to get this data programmatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option A: DIY in Python
&lt;/h2&gt;

&lt;p&gt;See the wall first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.indeed.com/jobs?q=software+engineer&amp;amp;l=New+York&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User-Agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mozilla/5.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# often 403 / Cloudflare challenge
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you do get HTML back, the job cards parse roughly like this (selectors change often, so re-check against the live page):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bs4&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BeautifulSoup&lt;/span&gt;

&lt;span class="n"&gt;soup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BeautifulSoup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;html.parser&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;soup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;div.job_seen_beacon&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select_one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;h2.jobTitle&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;comp&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select_one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[data-testid=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;company-name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sal&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select_one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[data-testid=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;attribute_snippet_testid&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;comp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;comp&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;sal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sal&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# raw string, still needs parsing
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you still have to normalize that salary string into numbers, follow each job to its full description, and scrape the company page separately. For one search that is fine. For a pipeline, the upkeep adds up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option B: the no-code / API shortcut
&lt;/h2&gt;

&lt;p&gt;When you just want clean rows, the &lt;a href="https://apify.com/factden/indeed-jobs-scraper?fpr=factden" rel="noopener noreferrer"&gt;Indeed Jobs Scraper&lt;/a&gt; on Apify talks to Indeed's structured endpoints (no fragile HTML path), handles the blocking, and returns parsed JSON. No login, no proxy setup.&lt;/p&gt;

&lt;p&gt;From Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ApifyClient&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;YOUR_APIFY_TOKEN&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;factden/indeed-jobs-scraper&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;software engineer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;location&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;New York, NY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;country&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;US&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxItems&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;salaryMin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;120000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;# native salary-range filter
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;scrapeCompany&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# attach company profiles for free
&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;company&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;salaryMin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;salaryMax&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set &lt;code&gt;country&lt;/code&gt; to any of 40+ codes (&lt;code&gt;US&lt;/code&gt;, &lt;code&gt;GB&lt;/code&gt;, &lt;code&gt;IN&lt;/code&gt;, &lt;code&gt;DE&lt;/code&gt;, &lt;code&gt;SG&lt;/code&gt;, ...), filter by &lt;code&gt;remote&lt;/code&gt;, &lt;code&gt;datePosted&lt;/code&gt;, &lt;code&gt;jobType&lt;/code&gt;, or &lt;code&gt;experienceLevel&lt;/code&gt;, or pass &lt;code&gt;startUrls&lt;/code&gt; / &lt;code&gt;jobKeys&lt;/code&gt; to fetch specific listings.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes back: 26 structured fields per job
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Group&lt;/th&gt;
&lt;th&gt;Fields&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Core&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;title&lt;/code&gt;, &lt;code&gt;company&lt;/code&gt;, &lt;code&gt;location&lt;/code&gt;, &lt;code&gt;city&lt;/code&gt;, &lt;code&gt;state&lt;/code&gt;, &lt;code&gt;countryCode&lt;/code&gt;, &lt;code&gt;url&lt;/code&gt;, &lt;code&gt;jobKey&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Salary&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;salary&lt;/code&gt; (raw), &lt;code&gt;salaryMin&lt;/code&gt;, &lt;code&gt;salaryMax&lt;/code&gt;, &lt;code&gt;salaryPeriod&lt;/code&gt;, &lt;code&gt;currency&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job detail&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;jobType&lt;/code&gt;, &lt;code&gt;remote&lt;/code&gt;, &lt;code&gt;occupations&lt;/code&gt;, &lt;code&gt;benefits&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;, &lt;code&gt;datePosted&lt;/code&gt;, &lt;code&gt;isUrgentHire&lt;/code&gt;, &lt;code&gt;easyApply&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Geo&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;latitude&lt;/code&gt;, &lt;code&gt;longitude&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Company (with &lt;code&gt;scrapeCompany&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;companyRating&lt;/code&gt;, &lt;code&gt;companyReviewCount&lt;/code&gt;, &lt;code&gt;companyPageUrl&lt;/code&gt; + a full company profile (industry, size, revenue, founded, website, socials)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A trimmed sample row:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Senior Software Engineer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"company"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Plaid"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"New York, NY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"salary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$120,000 - $160,000 a year"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"salaryMin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;120000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"salaryMax"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;160000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"salaryPeriod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"year"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"jobType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Full-time"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"remote"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"remote"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"benefits"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Health insurance"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"401(k)"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"datePosted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-06-10"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"companyRating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.indeed.com/viewjob?jk=6d50b3ebeb3fb122"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"jobKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"6d50b3ebeb3fb122"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The numeric salary fields and the on-demand company profile are what make this usable for labor-market analysis without a cleanup pass. Full field list and snippets are in the &lt;a href="https://github.com/factden/indeed-jobs-scraper" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grab a free sample dataset
&lt;/h2&gt;

&lt;p&gt;Want to see the data first? There is a free Indeed jobs sample (CSV/JSON) here: &lt;strong&gt;&lt;a href="https://factden.com/sample-indeed" rel="noopener noreferrer"&gt;factden.com/sample-indeed&lt;/a&gt;&lt;/strong&gt;. Drop it into pandas and the parsed salary columns are ready to plot.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is scraping Indeed legal?&lt;/strong&gt; The listings are publicly visible. As with any scraping, check Indeed's Terms of Service and your local rules (especially around personal data), and use the data responsibly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does Indeed have an API?&lt;/strong&gt; Not a usable one. Indeed closed its public job-search API to new developers, which is exactly why people scrape the public pages now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I stop getting blocked?&lt;/strong&gt; Residential proxies, real headers, and slow pacing, or a tool that talks to Indeed's structured endpoints instead of scraping HTML. Plain requests to the search pages will get challenged.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I get salary as numbers?&lt;/strong&gt; Yes. &lt;code&gt;salaryMin&lt;/code&gt;, &lt;code&gt;salaryMax&lt;/code&gt;, &lt;code&gt;salaryPeriod&lt;/code&gt;, and &lt;code&gt;currency&lt;/code&gt; are parsed from the raw string, so you can filter and aggregate directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I get company data too?&lt;/strong&gt; Yes. Turn on &lt;code&gt;scrapeCompany&lt;/code&gt; and each job carries the company's rating, size, industry, revenue, and profile links, no second scrape needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which countries are supported?&lt;/strong&gt; 40+, including US, UK, Canada, India, Germany, Singapore, Australia, and more, via the &lt;code&gt;country&lt;/code&gt; code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Doing competitive intelligence instead of hiring data? See &lt;a href="https://dev.to/factden/i-scraped-50000-g2-reviews-to-map-the-2026-saas-battlecard-atlas-12jg"&gt;how to scrape G2 reviews&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Other FactDen scrapers: &lt;a href="https://apify.com/factden/g2-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;G2 software reviews&lt;/a&gt; and &lt;a href="https://apify.com/factden/ctrip-trip-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Trip.com and Ctrip hotel reviews&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Questions, or a field you wish it extracted? Drop a comment.&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>python</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Scrape G2 Reviews in 2026 (Python + a No-Code Shortcut)</title>
      <dc:creator>Factden</dc:creator>
      <pubDate>Tue, 14 Jul 2026 07:52:34 +0000</pubDate>
      <link>https://dev.to/factden/how-to-scrape-g2-reviews-in-2026-python-a-no-code-shortcut-18eb</link>
      <guid>https://dev.to/factden/how-to-scrape-g2-reviews-in-2026-python-a-no-code-shortcut-18eb</guid>
      <description>&lt;p&gt;If you have tried to &lt;strong&gt;scrape G2 reviews&lt;/strong&gt; with a quick &lt;code&gt;requests.get()&lt;/code&gt;, you already know how it goes: a &lt;code&gt;403&lt;/code&gt;, a CAPTCHA, or a blank page. G2 is one of the tougher public sites to pull at scale. But the data behind it (ratings, structured pros and cons, and which competitor a reviewer switched away from and why) is worth the trouble if you do any competitive intelligence.&lt;/p&gt;

&lt;p&gt;This guide covers what a G2 review actually contains, why the naive approach fails, a Python path that works, a no-code shortcut, and a plain comparison so you can pick the right route. If you want a deeper field reference, there is a longer &lt;a href="https://factden.com/blog/how-to-scrape-g2-reviews" rel="noopener noreferrer"&gt;guide to scraping G2 reviews&lt;/a&gt; too.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a G2 review actually contains
&lt;/h2&gt;

&lt;p&gt;One &lt;a href="https://www.g2.com" rel="noopener noreferrer"&gt;G2&lt;/a&gt; review holds more than it looks. Per review you can pull:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Overall rating&lt;/strong&gt; (1 to 5) plus &lt;strong&gt;six sub-ratings&lt;/strong&gt; (ease of use, ease of setup, quality of support, meets requirements, and more)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structured pros, cons, and "problems solved"&lt;/strong&gt; as separate fields, not one text blob&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Switching data&lt;/strong&gt;: the competitor the reviewer came from, and why they left (the field most people are actually after)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reviewer context&lt;/strong&gt;: industry, role, company size, country&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dates, verification, and the incentivized flag&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The sub-ratings and switching fields are the reason to scrape G2 instead of skimming it by hand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why scraping G2 is hard: DataDome
&lt;/h2&gt;

&lt;p&gt;G2 runs &lt;a href="https://datadome.co/" rel="noopener noreferrer"&gt;DataDome&lt;/a&gt; bot protection behind a Cloudflare CDN. In practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A plain &lt;code&gt;requests&lt;/code&gt; or &lt;code&gt;httpx&lt;/code&gt; GET comes back &lt;code&gt;403&lt;/code&gt; almost immediately.&lt;/li&gt;
&lt;li&gt;Swapping the &lt;code&gt;User-Agent&lt;/code&gt; does nothing. DataDome fingerprints TLS, headers, and behavior.&lt;/li&gt;
&lt;li&gt;Datacenter IPs get flagged fast. You need residential proxies plus a real browser fingerprint, or a scraping API that brings both.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can absolutely scrape G2 yourself. The hard part is doing it reliably, at scale, without babysitting proxies. That maintenance tax is bigger than the parsing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three ways to get G2 review data
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;DIY Python&lt;/th&gt;
&lt;th&gt;G2 Reviews Scraper (actor)&lt;/th&gt;
&lt;th&gt;G2 Official API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Setup time&lt;/td&gt;
&lt;td&gt;Hours to days&lt;/td&gt;
&lt;td&gt;~30 seconds&lt;/td&gt;
&lt;td&gt;Weeks (sales + contract)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Anti-bot handled&lt;/td&gt;
&lt;td&gt;You (proxies + browser)&lt;/td&gt;
&lt;td&gt;Built in&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Structured fields&lt;/td&gt;
&lt;td&gt;You parse them&lt;/td&gt;
&lt;td&gt;27, incl. switching data&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sub-ratings + "switched from"&lt;/td&gt;
&lt;td&gt;Hard to parse&lt;/td&gt;
&lt;td&gt;Yes, resolved to product names&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;Proxies + eng time&lt;/td&gt;
&lt;td&gt;$0.004/row, ~1,250 free&lt;/td&gt;
&lt;td&gt;Enterprise contract&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;One-off, few pages&lt;/td&gt;
&lt;td&gt;Scheduled, at scale&lt;/td&gt;
&lt;td&gt;Large enterprises&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Option A: DIY in Python
&lt;/h2&gt;

&lt;p&gt;See the wall for yourself first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.g2.com/products/slack/reviews&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User-Agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mozilla/5.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# 403 (DataDome)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Getting past it needs a residential proxy and a browser-grade fingerprint (a stealth headless browser, or a scraping API). Once you have the HTML, parsing review cards with BeautifulSoup looks roughly like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bs4&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BeautifulSoup&lt;/span&gt;

&lt;span class="n"&gt;soup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BeautifulSoup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;html.parser&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;soup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[itemprop=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;review&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;rating&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select_one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[itemprop=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ratingValue&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select_one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[itemprop=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select_one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[itemprop=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;reviewBody&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;rating&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;rating&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)[:&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things that will cost you time:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Selectors drift. G2 changes its markup, so re-check against the live page.&lt;/li&gt;
&lt;li&gt;Sub-ratings and switching data sit in nested markup that is fiddly to parse consistently.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;DIY is fine for a one-off across a handful of pages. For hundreds of products on a schedule, the proxy and anti-bot upkeep usually costs more than it saves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option B: the no-code / API shortcut
&lt;/h2&gt;

&lt;p&gt;When you just want clean rows, the &lt;a href="https://apify.com/factden/g2-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;G2 Reviews Scraper&lt;/a&gt; on Apify handles the DataDome, proxy, and parsing layer and hands back structured JSON. No login, no proxy setup.&lt;/p&gt;

&lt;p&gt;From Python, using the Apify client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ApifyClient&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;YOUR_APIFY_TOKEN&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;factden/g2-reviews-scraper&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reviews&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;startUrls&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.g2.com/products/slack/reviews&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;notion&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxReviewsPerProduct&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sortReviews&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;helpful&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;overallRating&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reviewTitle&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;previousCompetitors&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pass full product URLs or bare slugs (&lt;code&gt;notion&lt;/code&gt;, &lt;code&gt;slack&lt;/code&gt;). There is also a Products mode that finds competitor products by keyword before you pull their reviews.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes back: 27 structured fields
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Group&lt;/th&gt;
&lt;th&gt;Fields&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ratings&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;overallRating&lt;/code&gt;, &lt;code&gt;subRatings&lt;/code&gt; (ease of use, setup, support, meets requirements)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Structured text&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;pros&lt;/code&gt;, &lt;code&gt;cons&lt;/code&gt;, &lt;code&gt;problemsSolved&lt;/code&gt;, &lt;code&gt;recommendations&lt;/code&gt;, &lt;code&gt;reviewText&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Switching / battlecard&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;didSwitchFromCompetitor&lt;/code&gt;, &lt;code&gt;previousCompetitors&lt;/code&gt;, &lt;code&gt;whySwitched&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reviewer&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;reviewerIndustry&lt;/code&gt;, &lt;code&gt;reviewerRole&lt;/code&gt;, &lt;code&gt;companySize&lt;/code&gt;, &lt;code&gt;reviewerCountry&lt;/code&gt;, &lt;code&gt;reviewerName&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Meta&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;isIncentivized&lt;/code&gt;, &lt;code&gt;helpfulVotes&lt;/code&gt;, &lt;code&gt;submittedAt&lt;/code&gt;, &lt;code&gt;reviewUrl&lt;/code&gt;, &lt;code&gt;productSlug&lt;/code&gt;, &lt;code&gt;productName&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI-ready&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;markdownContent&lt;/code&gt; (a self-contained markdown block per review, for RAG / vector DBs)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A trimmed sample row, so you can see the shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"productName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Slack"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"overallRating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"subRatings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"easeOfUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"easeOfSetup"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"qualityOfSupport"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"meetsRequirements"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reviewTitle"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Runs our whole company"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pros"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Channels keep every project in one place."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cons"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Notifications get noisy at scale."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"didSwitchFromCompetitor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"previousCompetitors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Microsoft Teams"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"whySwitched"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Better threads and a faster mobile app."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"companySize"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mid-Market"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reviewerRole"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"IT Administrator"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"isIncentivized"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The two fields most scrapers drop, &lt;code&gt;previousCompetitors&lt;/code&gt; + &lt;code&gt;whySwitched&lt;/code&gt; (resolved to real product names) and the per-dimension &lt;code&gt;subRatings&lt;/code&gt;, are what make this useful for battlecards. Full field list and copy-paste snippets are in the &lt;a href="https://github.com/factden/g2-reviews-scraper" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grab a free sample dataset
&lt;/h2&gt;

&lt;p&gt;Want to see the data before running anything? There is a free G2 review sample (CSV/JSON) here: &lt;strong&gt;&lt;a href="https://factden.com/sample" rel="noopener noreferrer"&gt;factden.com/sample&lt;/a&gt;&lt;/strong&gt; (also mirrored on HuggingFace and Kaggle). Load it into pandas and the sub-ratings and switching fields show up straight away.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is scraping G2 reviews legal?&lt;/strong&gt; The reviews are publicly available. As with any scraping, check G2's Terms of Service and your local rules (GDPR and similar for personal data), and use the data responsibly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does G2 have an API?&lt;/strong&gt; Yes, but it is enterprise-tier: a sales call, a contract, and procurement. For most teams, scraping the public pages, or using a ready-made actor, is the faster route to the same public data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I stop getting blocked?&lt;/strong&gt; Residential proxies, a real browser fingerprint, and human-like pacing, or a tool that bundles all three. Datacenter IPs and plain requests will not survive DataDome.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How much does it cost?&lt;/strong&gt; DIY costs proxies plus your time. The actor is pay-per-result at $0.004 per row, with about 1,250 rows free on Apify's $5 new-account credit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I get the "switched from" competitor data?&lt;/strong&gt; Yes. That is the &lt;code&gt;previousCompetitors&lt;/code&gt; and &lt;code&gt;whySwitched&lt;/code&gt; fields, resolved to product names. It is the reason most people scrape G2 in the first place.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I filter out incentivized (gift-card) reviews?&lt;/strong&gt; Yes. Every row carries an &lt;code&gt;isIncentivized&lt;/code&gt; flag, so you can recompute a rating on organic reviews only. G2's own UI will not let you do that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;I mapped switching and sub-ratings across 25 tools in &lt;a href="https://dev.to/factden/i-scraped-50000-g2-reviews-to-map-the-2026-saas-battlecard-atlas-12jg"&gt;the 2026 SaaS Battlecard Atlas&lt;/a&gt; (50,000 reviews).&lt;/li&gt;
&lt;li&gt;Other FactDen scrapers: &lt;a href="https://apify.com/factden/indeed-jobs-scraper?fpr=factden" rel="noopener noreferrer"&gt;Indeed jobs and salary data&lt;/a&gt; and &lt;a href="https://apify.com/factden/ctrip-trip-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Trip.com and Ctrip hotel reviews&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Questions, or a field you wish it extracted? Drop a comment.&lt;/p&gt;

&lt;p&gt;Found this useful? A ⭐ on the GitHub repo (&lt;a href="https://github.com/factden/g2-reviews-scraper" rel="noopener noreferrer"&gt;https://github.com/factden/g2-reviews-scraper&lt;/a&gt;) helps other people find it.&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>python</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>I scraped 50,000 G2 reviews to map the 2026 SaaS Battlecard Atlas</title>
      <dc:creator>Factden</dc:creator>
      <pubDate>Mon, 01 Jun 2026 10:30:32 +0000</pubDate>
      <link>https://dev.to/factden/i-scraped-50000-g2-reviews-to-map-the-2026-saas-battlecard-atlas-12jg</link>
      <guid>https://dev.to/factden/i-scraped-50000-g2-reviews-to-map-the-2026-saas-battlecard-atlas-12jg</guid>
      <description>&lt;h2&gt;
  
  
  The 2026 SaaS Battlecard Atlas: What 50,000 G2 Reviews Reveal About 25 of the Most-Used B2B Tools
&lt;/h2&gt;

&lt;p&gt;A few weeks ago I got tired of guessing which B2B SaaS tools were actually worth recommending. G2 has the reviews, but reading 50,000 of them by hand isn't a great use of a weekend.&lt;/p&gt;

&lt;p&gt;So I built a scraper. Then I ran it across 25 of the most-reviewed B2B tools on G2. Then I let pandas do the rest.&lt;/p&gt;

&lt;p&gt;What came out surprised me in a few places. Here's the full breakdown.&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR: 5 things B2B buyers should care about
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Gong is the most-loved tool in the dataset.&lt;/strong&gt; 4.88 stars overall, and the sub-ratings are all consistently high. Most "4.8 star" SaaS tools have one weak sub-rating they hide. Gong doesn't.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The biggest customer migration of 2026 is Mailchimp going to ActiveCampaign.&lt;/strong&gt; 350 named-switching events from Mailchimp variants flowing into ActiveCampaign. Email marketing is consolidating, fast.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apollo is winning the sales intelligence war.&lt;/strong&gt; 88 customers named ZoomInfo (GTM Workspace) as their previous tool. That's the largest single sales-intel migration in the whole dataset.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UX is both the #1 thing customers praise AND the #1 thing they complain about.&lt;/strong&gt; Mentioned in 49% of pros and 20% of cons. If you nail the UI, customers will love you for it. If you don't, they will not shut up about it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rippling, Deel, and Gong have the lowest "incentivization gaming" rates (3.5% to 11%).&lt;/strong&gt; Meaning their high ratings are organic, not bought with gift cards. If you trust one tool's 4.8 stars, trust theirs.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  How I built this dataset
&lt;/h2&gt;

&lt;p&gt;I used &lt;a href="https://apify.com/factden/g2-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;g2-reviews-scraper&lt;/a&gt;, an Apify actor I built specifically for this. It extracts 32 structured fields per review including ratings, sub-ratings, switching data, structured pros/cons, demographics, and an LLM-ready markdown block. I wrote up the full method separately in &lt;a href="https://factden.com/blog/how-to-scrape-g2-reviews" rel="noopener noreferrer"&gt;how to scrape G2 reviews&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The numbers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Reviews analyzed&lt;/td&gt;
&lt;td&gt;50,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Products&lt;/td&gt;
&lt;td&gt;25 (top-reviewed across 7 B2B categories)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Time range&lt;/td&gt;
&lt;td&gt;June 2025 to May 2026 (last 12 months)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fields per review&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reviewer demographics populated&lt;/td&gt;
&lt;td&gt;99% country, 93% company size, 85% industry, 76% role&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Named switching events&lt;/td&gt;
&lt;td&gt;6,004&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Customer quotes (whySwitched)&lt;/td&gt;
&lt;td&gt;5,143&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Product list&lt;/strong&gt; (25 unique, vendor-deduped): Zoom Workplace, Google Workspace, Slack, Microsoft Teams, Miro, Salesforce Sales Cloud, HubSpot Sales Hub, Apollo.io, ZoomInfo (GTM Workspace), Salesloft, Gong, ActiveCampaign, Mailchimp Email, Hootsuite, monday.com, Asana, Trello, ClickUp, Smartsheet, Notion, Jira, Zendesk, Zoho Desk, Rippling, Deel.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. What each tool is actually good at
&lt;/h2&gt;

&lt;p&gt;This is the chart every B2B buyer should screenshot before their next vendor selection. Greener cells mean the tool scores better on that sub-rating.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwnkja1i0dlv35i6s56p6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwnkja1i0dlv35i6s56p6.png" alt="Sub-Rating Matrix" width="800" height="732"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A few things jumped out when I built this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rippling, Deel, and Gong dominate every column.&lt;/strong&gt; It's rare to see consistency this clean. Most products win on one or two dimensions and lose on others. These three just win, everywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smartsheet and ActiveCampaign show up at the bottom across the board.&lt;/strong&gt; Even their "overall" ratings hide weaker sub-rating performance. Buyers should not be fooled by the headline star count.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quality of Support is the universal weakest sub-rating.&lt;/strong&gt; Even the category leaders like Salesforce (6.14), HubSpot (5.96), and Zendesk (5.99) lag here. Don't expect great support from any of them; budget for your own enablement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;easeOfDoingBusinessWith has wide variance.&lt;/strong&gt; That column captures procurement friction, contract negotiation, billing weirdness. If yours is a hidden buyer-pain category, you'd see it here.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bottom line for buyers: don't trust the overall star. Look at the sub-rating that matters for your use case.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. The 2026 Productivity Heroes
&lt;/h2&gt;

&lt;p&gt;Five tools customers genuinely love. High overall rating combined with low "incentivization gaming" (meaning the ratings reflect organic praise, not paid reviews).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiikeebhiexu4q05f6k9n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiikeebhiexu4q05f6k9n.png" alt="Productivity Heroes" width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rank&lt;/th&gt;
&lt;th&gt;Product&lt;/th&gt;
&lt;th&gt;Rating&lt;/th&gt;
&lt;th&gt;Incentivization&lt;/th&gt;
&lt;th&gt;Why it wins&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Gong&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4.88&lt;/td&gt;
&lt;td&gt;11.4%&lt;/td&gt;
&lt;td&gt;Revenue Operations leader. Highest sub-ratings across the board.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Rippling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4.84&lt;/td&gt;
&lt;td&gt;3.5%&lt;/td&gt;
&lt;td&gt;Payroll/HR with the cleanest organic praise rate in the dataset.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Deel&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4.81&lt;/td&gt;
&lt;td&gt;5.5%&lt;/td&gt;
&lt;td&gt;Contractor payments. Strong on ease of doing business plus support (6.45).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;monday.com&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4.75&lt;/td&gt;
&lt;td&gt;7.8%&lt;/td&gt;
&lt;td&gt;Best PM score combined with low incentivization.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;ZoomInfo (GTM Workspace)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4.74&lt;/td&gt;
&lt;td&gt;8.2%&lt;/td&gt;
&lt;td&gt;Sales intelligence. Surprisingly strong in a crowded category.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Here's the counter-intuitive part. The most-incentivized tools in the dataset (Mailchimp at 78%, Google Workspace at 71%, Trello at 65%) all rate between 4.5 and 4.8 stars. I expected the bought reviews to be inflating ratings dramatically.&lt;/p&gt;

&lt;p&gt;They aren't. When you filter out the incentivized reviews, &lt;strong&gt;the ratings move by less than 0.1 star&lt;/strong&gt; in our sample. The biggest shift I observed was 0.08 for Mailchimp Email. The signal is real even with the noise. (Section 7 has the full breakdown.)&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Who's winning customers, and who's losing them
&lt;/h2&gt;

&lt;p&gt;This was the chart I most wanted to build. The data comes from 6,004 named-switching events where a reviewer explicitly mentioned the tool they came from.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5mzj2wx8n5k2gpkfrvs4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5mzj2wx8n5k2gpkfrvs4.png" alt="Net Migration Leaderboard" width="800" height="766"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Green bars are vendors gaining customers (net positive switching flow). Red bars are vendors losing them. The annotation on the right shows each vendor's primary source (for winners) or destination (for losers).&lt;/p&gt;

&lt;h3&gt;
  
  
  Top 5 winners
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Vendor&lt;/th&gt;
&lt;th&gt;Net flow&lt;/th&gt;
&lt;th&gt;Where they're gaining from&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ActiveCampaign&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;+536&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Mailchimp Email (251) and Mailchimp All-in-One Platform (99). Total dominance.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ClickUp&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;+219&lt;/td&gt;
&lt;td&gt;Asana (69) and others. Leading PM consolidation.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Zoho Desk&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;+133&lt;/td&gt;
&lt;td&gt;Freshdesk (80) and other help-desk tools.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hootsuite&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;+107&lt;/td&gt;
&lt;td&gt;Sprout Social (65). Surprising given Hootsuite's overall positioning.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;HubSpot Sales&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;+101&lt;/td&gt;
&lt;td&gt;Salesforce Sales (50). CRM mid-market shift.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Top 5 losers
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Vendor&lt;/th&gt;
&lt;th&gt;Net flow&lt;/th&gt;
&lt;th&gt;Where they're losing to&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mailchimp Email&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;-215&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Almost entirely to ActiveCampaign.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trello&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-137&lt;/td&gt;
&lt;td&gt;Mostly to ClickUp.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Freshdesk&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-108&lt;/td&gt;
&lt;td&gt;Mostly to Zoho Desk.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mailchimp Platform&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-99&lt;/td&gt;
&lt;td&gt;Also to ActiveCampaign.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ZoomInfo (GTM)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-88&lt;/td&gt;
&lt;td&gt;All to Apollo.io.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The 5 biggest disruption stories
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhmrccm1ioh7qket2kg8r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhmrccm1ioh7qket2kg8r.png" alt="5 Big Disruptions" width="799" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you read nothing else from this report, these 5 named migrations are the story of B2B SaaS in 2026:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Email Marketing&lt;/strong&gt;: Mailchimp Email to ActiveCampaign (251 switches). The single largest named migration in the dataset.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sales Intelligence&lt;/strong&gt;: ZoomInfo (GTM) to Apollo.io (88 switches). The rising challenger is winning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customer Support&lt;/strong&gt;: Freshdesk to Zoho Desk (80 switches). Cost-conscious buyers are shifting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project Management&lt;/strong&gt;: Asana to ClickUp (69 switches). ClickUp is eating Asana's mid-market.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sales Engagement&lt;/strong&gt;: Outreach to Salesloft (65 switches). Salesloft pulling ahead.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you're currently using a "loser" vendor, look at where their customers are going. Those are the tools to evaluate. If you're on a "winner" vendor, you're aligned with where the market is moving.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. What customers complain about most
&lt;/h2&gt;

&lt;p&gt;What people say in the &lt;code&gt;cons&lt;/code&gt; field, across all 50,000 reviews.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fazcmzz7wf022cy6cwhv4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fazcmzz7wf022cy6cwhv4.png" alt="Pain Atlas" width="800" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Top 5 universal complaints:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;UX/UI&lt;/strong&gt; (20.3% of reviews). The single most-complained-about issue across all B2B SaaS. Interfaces, intuitiveness, design. Customers notice when it's bad.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learning curve&lt;/strong&gt; (13.1%). Onboarding pain is universal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mobile&lt;/strong&gt; (9.9%). Mobile apps consistently disappoint.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt; (8.6%). Speed, lag, crashes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrations&lt;/strong&gt; (8.1%). Gaps in connecting to other tools.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For PMMs and sales enablement teams: these themes show up in 4,000 to 10,000 reviews each. That's a goldmine of objection-handling material. The actor exposes the structured &lt;code&gt;cons&lt;/code&gt; field for any product, so you can mine these patterns for your own competitor set.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. What customers actually praise
&lt;/h2&gt;

&lt;p&gt;The other half of the story. What people say in the &lt;code&gt;pros&lt;/code&gt; field, across all 50,000 reviews.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiszvsnap2j6vjs2s8vu9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiszvsnap2j6vjs2s8vu9.png" alt="Voice of Customer" width="800" height="839"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Top 5 universal pros:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;UX/UI&lt;/strong&gt; (49.4%). Also the most-praised dimension. Half of all customers compliment the interface.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mobile&lt;/strong&gt; (18.2%). When mobile works, customers notice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrations&lt;/strong&gt; (18.0%). Same theme as cons, but framed positively.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customer support&lt;/strong&gt; (7.6%). When it's good, customers are deeply grateful.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customization&lt;/strong&gt; (7.6%). Flexible products win loyalty.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The UX paradox&lt;/strong&gt;: UX shows up in 49% of pros and 20% of cons. That's not a contradiction. That's the signal. &lt;strong&gt;UX is the single biggest lever in B2B SaaS satisfaction.&lt;/strong&gt; Companies that get it right generate 2.4 times more positive mentions than the ones that don't.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. The real customer voice (direct quotes)
&lt;/h2&gt;

&lt;p&gt;These are pulled straight from the &lt;code&gt;whySwitched&lt;/code&gt; field. Real customer language explaining their actual decisions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Gmail was much easier to administer than on-prem Exchange."&lt;/em&gt;&lt;br&gt;
Mid-Market reviewer switching to Google Workspace&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Integrations and its customizable templates."&lt;/em&gt;&lt;br&gt;
Asana reviewer who came from monday.com&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"It has better project management features."&lt;/em&gt;&lt;br&gt;
monday.com reviewer who came from Hootsuite&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"For individual use Trello is faster."&lt;/em&gt;&lt;br&gt;
Trello reviewer who came from Jira&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Better thread management and a more responsive mobile app."&lt;/em&gt;&lt;br&gt;
Slack reviewer who came from Microsoft Teams&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the customer language sales teams pay competitive intelligence firms thousands for. It's all in the raw data, queryable by competitor.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. About those incentivized reviews
&lt;/h2&gt;

&lt;p&gt;One important thing buyers should know: &lt;strong&gt;25.5% of the G2 reviews in our sample are incentivized.&lt;/strong&gt; The vendor provided a gift card, charitable donation, or similar perk in exchange for the review. This rate varies wildly by vendor, from 3.5% (Rippling) to 78% (Mailchimp).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbf3q3gnhuxko2clqrih2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbf3q3gnhuxko2clqrih2.png" alt="Filtered vs Unfiltered Rating Dumbbell Chart" width="800" height="686"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's the good news: when you filter out the incentivized reviews, &lt;strong&gt;most products' ratings move by less than 0.1 star&lt;/strong&gt;. The biggest shift I observed was 0.08 (Mailchimp Email). The rating gap between products is mostly genuine.&lt;/p&gt;

&lt;p&gt;Here's the actionable news: G2 doesn't expose the "isIncentivized" flag as a filter in their own UI. The actor does. You can do this calibration yourself for any vendor by filtering out the incentivized reviews and seeing how the rating shifts. It's a one-click toggle in the actor's output.&lt;/p&gt;

&lt;p&gt;This is exactly the kind of analysis competitive intelligence and procurement teams do. Now anyone can do it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Want the dataset?
&lt;/h2&gt;

&lt;p&gt;A balanced sample of 2,500 reviews (100 per product across all 25 tools) is available as a free CSV. All 32 fields populated. Leave an email and the download starts immediately on submit.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://factden.com/sample" rel="noopener noreferrer"&gt;Download the SaaS Battlecard Atlas dataset (free CSV)&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For the complete dataset of your own competitor set, run the actor yourself.&lt;/p&gt;




&lt;h2&gt;
  
  
  Methodology
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scraper&lt;/strong&gt;: &lt;a href="https://apify.com/factden/g2-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;factden/g2-reviews-scraper&lt;/a&gt;, an open Apify actor that extracts G2 reviews with 32 structured fields including switching data and LLM-ready markdown.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sample selection&lt;/strong&gt;: top-reviewed products by G2 review count, vendor-deduplicated, across 7 B2B categories. 25 products times 2,000 reviews each equals 50,000 reviews.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Date filter&lt;/strong&gt;: 2025-06-01 to 2026-05-31 (recent customer voice).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Switching analysis&lt;/strong&gt;: from the &lt;code&gt;previousCompetitors&lt;/code&gt; field (12.0% population) and the per-product &lt;code&gt;topCompetitors&lt;/code&gt; aggregate (250 ranked competitor relationships).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Theme mining&lt;/strong&gt;: keyword-based pattern matching across pros/cons/whySwitched fields. Conservative, so false negatives are more likely than false positives.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limitations&lt;/strong&gt;: G2's data has known biases (US-skew, B2B-skew, incentivization variance). I've quantified incentivization rates per product; other biases remain as caveats.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Build your own Battlecard Atlas
&lt;/h2&gt;

&lt;p&gt;Want to do this analysis on &lt;strong&gt;your specific competitor set&lt;/strong&gt;? Run the G2 Reviews Scraper:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://apify.com/factden/g2-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Try the G2 Reviews Scraper free&lt;/a&gt;&lt;/strong&gt; (first 1,250 reviews free on Apify's $5 monthly credit)&lt;/p&gt;

&lt;p&gt;What you get out of one run:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All 32 fields per review (ratings, sub-ratings, structured pros/cons, switching history, demographics, LLM-ready markdown)&lt;/li&gt;
&lt;li&gt;Pre-aggregated &lt;code&gt;topCompetitors&lt;/code&gt; per product&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;isIncentivized&lt;/code&gt; flag for filtering&lt;/li&gt;
&lt;li&gt;CSV / JSON / Excel export&lt;/li&gt;
&lt;li&gt;Apify Schedules + Webhooks for ongoing monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Built for: competitive intelligence analysts, RevOps teams, product marketers, sales enablement, AI/RAG engineers ingesting review data, and procurement teams doing due diligence. More structured-data actors at &lt;a href="https://factden.com" rel="noopener noreferrer"&gt;FactDen&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Working in other verticals? I built the same structured-data approach for &lt;a href="https://apify.com/factden/ctrip-trip-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Trip.com &amp;amp; Ctrip hotel reviews&lt;/a&gt; and &lt;a href="https://apify.com/factden/indeed-jobs-scraper?fpr=factden" rel="noopener noreferrer"&gt;Indeed jobs &amp;amp; salary data&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;If you found something interesting in the data, or you want me to run this on a different competitor set, drop a comment or email me at &lt;a href="mailto:hello@factden.com"&gt;hello@factden.com&lt;/a&gt;. The full dataset is a &lt;a href="https://factden.com/sample" rel="noopener noreferrer"&gt;free CSV download&lt;/a&gt;. If you want to replicate this analysis for your own space, the actor is the fastest path. &lt;a href="https://apify.com/factden/g2-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;First 1,250 reviews are free.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>ai</category>
      <category>dataanalysis</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
