<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>client-side on S Anand</title>
    <link>https://www.s-anand.net/blog/tag/client-side/</link>
    <description>Recent content in client-side on S Anand</description>
    <generator>Hugo -- 0.164.0</generator>
    <language>en-us</language>
    <lastBuildDate>Fri, 22 Nov 2024 10:38:11 +0000</lastBuildDate>
    <atom:link href="https://www.s-anand.net/blog/tag/client-side/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Why don&#39;t students hack exams when they can?</title>
      <link>https://www.s-anand.net/blog/why-dont-students-hack-exams-when-they-can/</link>
      <pubDate>Fri, 22 Nov 2024 10:38:09 +0000</pubDate>
      <guid>https://www.s-anand.net/blog/why-dont-students-hack-exams-when-they-can/</guid>
      <description>&lt;p&gt;&lt;img alt=&#34;Why don&amp;rsquo;t students hack exams when they can?&#34; loading=&#34;lazy&#34; src=&#34;https://www.s-anand.net/blog/assets/calvin-tries-to-cheat-an-exam.webp&#34;&gt;&lt;/p&gt;
&lt;p&gt;This year, I created a series of tests for &lt;a href=&#34;https://study.iitm.ac.in/ds/course_pages/BSSE2002.html&#34;&gt;my course at IITM&lt;/a&gt; and to recruit for &lt;a href=&#34;https://gramener.com/&#34;&gt;Gramener&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;The tests had 2 interesting features.&lt;/p&gt;
&lt;h3 class=&#34;wp-block-heading&#34;&gt;&lt;strong&gt;One question required them to hack the page&lt;/strong&gt;&lt;/h3&gt;
&lt;blockquote class=&#34;wp-block-quote&#34;&gt;
&lt;p&gt;Write the body of the request to an &lt;a href=&#34;https://platform.openai.com/docs/api-reference/chat&#34;&gt;OpenAI chat completion call&lt;/a&gt; that:&lt;/p&gt;
&lt;ul class=&#34;wp-block-list&#34;&gt;
&lt;li&gt;Uses model &lt;code&gt;gpt-4o-mini&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Has a &lt;code&gt;system&lt;/code&gt; message: &lt;code&gt;Respond in JSON&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Has a user message: &lt;code&gt;Generate 10 random addresses in the US&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Uses &lt;a href=&#34;https://platform.openai.com/docs/guides/structured-outputs/&#34;&gt;structured outputs&lt;/a&gt; to respond with an object &lt;code&gt;addresses&lt;/code&gt; which is an array of objects with &lt;strong&gt;required&lt;/strong&gt; fields: &lt;code&gt;street&lt;/code&gt; (string) &lt;code&gt;city&lt;/code&gt; (string) &lt;code&gt;apartment&lt;/code&gt; (string) .&lt;/li&gt;
&lt;li&gt;Sets &lt;code&gt;additionalProperties&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt; to prevent additional properties.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What is the JSON body we should send to &lt;code&gt;https://api.openai.com/v1/chat/completions&lt;/code&gt; for this? (No need to run it or to use an API key. Just write the body of the request below.)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;There&#39;s no answer box above. Figure out how to enable it. That&#39;s part of the test.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The only way to even &lt;em&gt;attempt&lt;/em&gt; this question is to inspect the page, find the hidden input and make it visible. (This requires removing a class, an attribute, and a style - from different places.)&lt;/p&gt;
&lt;p&gt;Here&#39;s the number of people who managed to enable the text box and answer it.&lt;/p&gt;
&lt;figure class=&#34;wp-block-table&#34;&gt;&lt;table class=&#34;has-fixed-layout&#34;&gt;&lt;thead&gt;&lt;tr&gt;&lt;th class=&#34;has-text-align-left&#34; data-align=&#34;left&#34;&gt;College&lt;/th&gt;&lt;th class=&#34;has-text-align-right&#34; data-align=&#34;right&#34;&gt;# students&lt;/th&gt;&lt;th class=&#34;has-text-align-right&#34; data-align=&#34;right&#34;&gt;Enabled&lt;/th&gt;&lt;th class=&#34;has-text-align-right&#34; data-align=&#34;right&#34;&gt;Answered&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&#34;has-text-align-left&#34; data-align=&#34;left&#34;&gt;NIT Bhopal&lt;/td&gt;&lt;td class=&#34;has-text-align-right&#34; data-align=&#34;right&#34;&gt;144&lt;/td&gt;&lt;td class=&#34;has-text-align-right&#34; data-align=&#34;right&#34;&gt;4 (2.8%)&lt;/td&gt;&lt;td class=&#34;has-text-align-right&#34; data-align=&#34;right&#34;&gt;0 (0.0%)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&#34;has-text-align-left&#34; data-align=&#34;left&#34;&gt;CBIT&lt;/td&gt;&lt;td class=&#34;has-text-align-right&#34; data-align=&#34;right&#34;&gt;277&lt;/td&gt;&lt;td class=&#34;has-text-align-right&#34; data-align=&#34;right&#34;&gt;16 (5.8%)&lt;/td&gt;&lt;td class=&#34;has-text-align-right&#34; data-align=&#34;right&#34;&gt;0 (0.0%)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class=&#34;has-text-align-left&#34; data-align=&#34;left&#34;&gt;IIT Madras&lt;/td&gt;&lt;td class=&#34;has-text-align-right&#34; data-align=&#34;right&#34;&gt;693&lt;/td&gt;&lt;td class=&#34;has-text-align-right&#34; data-align=&#34;right&#34;&gt;74 (10.7%)&lt;/td&gt;&lt;td class=&#34;has-text-align-right&#34; data-align=&#34;right&#34;&gt;4 (0.6%)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;A few things surprised me.&lt;/p&gt;
&lt;p&gt;First, I think &lt;strong&gt;students don&#39;t inspect HTML&lt;/strong&gt;. Less than 10% of students managed to modify the HTML page, &lt;em&gt;even after being told they need to&lt;/em&gt;. &lt;strong&gt;But they know web programming&lt;/strong&gt;. 49 students at CBIT scored full marks on the rest of the questions, which includes CSS selectors and complex JS code. Maybe editing in a browser instead of an editor is a big mental leap?&lt;/p&gt;
&lt;p&gt;Second, almost &lt;strong&gt;no one could solve this problem&lt;/strong&gt;. There are 3 ways to easily solve it.&lt;/p&gt;
&lt;ol class=&#34;wp-block-list&#34;&gt;
&lt;li&gt;Copy the question and relevant test cases from my exam page&#39;s JavaScript into ChatGPT and ask for an answer. (I test it and it works.)&lt;/li&gt;
&lt;li&gt;Copy the question and &lt;a href=&#34;https://platform.openai.com/docs/guides/structured-outputs&#34;&gt;structured output documentation&lt;/a&gt; to ChatGPT and ask for an answer. (I tested it and it works.)&lt;/li&gt;
&lt;li&gt;Create a random JSON and just keep fixing the errors manually until it passes. (The exam gives detailed error messages like &#34;The system message must be &#39;Respond in JSON&#39;&#34;, &#34;addresses items must be an object&#34;, etc.)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Maybe questions from a curriculum are easier to solve than questions not in a curriculum? Or is JSON schema too hard?&lt;/p&gt;
&lt;h3 class=&#34;wp-block-heading&#34;&gt;The exam was officially hackable&lt;/h3&gt;
&lt;p&gt;All validation was on the client side. The JS code was minified and answers are dynamically generated. But a student can set a breakpoint, see the answers, and modify their responses.&lt;/p&gt;
&lt;p&gt;The students at NIT Bhopal and CBIT were not explicitly told that. The students at IITM were &lt;em&gt;explicitly told&lt;/em&gt; that they could (and are welcome to) hack it.&lt;/p&gt;
&lt;p&gt;Out of the 1,114 students who took these tests, &lt;strong&gt;only one student actually hacked it&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;(How do I know that? No other student got full marks. This student got full marks with empty answers.)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;It&#39;s probably not that difficult&lt;/strong&gt;. My &lt;a href=&#34;https://github.com/sanand0/tools-in-data-science-public/blob/4f42624aadf58aca21b46d3a4b6e856c2ae85adf/2-data-sourcing.md#scraping-the-imdb-with-browser-javascript&#34;&gt;course content&lt;/a&gt; covers scraping pages using JavaScript using DevTools. Inspecting JS is just a step away.&lt;/p&gt;
&lt;p&gt;I did chat with the student who hacked it, asking:&lt;/p&gt;
&lt;blockquote class=&#34;wp-block-quote&#34;&gt;
&lt;p&gt;&lt;strong&gt;Anand&lt;/strong&gt;: How come you didn&#39;t share the details of the hack with others?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Student&lt;/strong&gt;: I did with a few but I am not sure whether or not they were able to figure it out still.&lt;br/&gt;Most students in the program still require a lot of handholding even with basic things.&lt;br/&gt;Experience from being a TA [Teaching Assistant] past term.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 class=&#34;wp-block-heading&#34;&gt;Why didn&#39;t they hack?&lt;/h3&gt;
&lt;p&gt;Maybe...&lt;/p&gt;
&lt;ol class=&#34;wp-block-list&#34;&gt;
&lt;li&gt;&lt;strong&gt;They don&#39;t believe me&lt;/strong&gt;. What if hacking the exam page is considered cheating, even if explicitly allowed?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The time pressure is too much&lt;/strong&gt;. They&#39;d rather solve what they know than risk wasting time hacking.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It feels wrong&lt;/strong&gt;. They&#39;d rather answer based on their knowledge than take a shortcut.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;They don&#39;t know how&lt;/strong&gt;. Using DevTools is more sophisticated than web programming.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Issue #1 - the trust issue - is solveable. We can issue multiple official notices.&lt;/p&gt;
&lt;p&gt;Issue #4 - capability - is not worth solving. My aim is to get students to do stuff they weren&#39;t taught.&lt;/p&gt;
&lt;p&gt;Issue #2 &amp;amp; #3 - &lt;strong&gt;a risk-taking culture&lt;/strong&gt; - is what I want to encourage. It might teach them to blur ethical lines and neglect fundamentals (which are bad), but it might also build adaptability, creativity, and prepare them for real-world scenarios.&lt;/p&gt;
&lt;p&gt;Personally, I need more team members that get the job done even if they&#39;ve never done it before.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Client side scraping</title>
      <link>https://www.s-anand.net/blog/client-side-scraping/</link>
      <pubDate>Wed, 04 Mar 2009 18:00:57 +0000</pubDate>
      <guid>https://www.s-anand.net/blog/client-side-scraping/</guid>
      <description>&lt;p&gt;“Scraping” is extracting content from a website. It’s often used to build something on top of the existing content. For example, I’ve built a site that &lt;a href=&#34;http://250.s-anand.net/&#34;&gt;tracks movies on the IMDb 250&lt;/a&gt; by scraping content.&lt;/p&gt;
&lt;p&gt;There are libraries that simplify &lt;a href=&#34;http://en.wikipedia.org/wiki/Web_scraping&#34;&gt;scraping&lt;/a&gt; in most languages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Perl: &lt;a href=&#34;http://www.perl.com/pub/a/2003/01/22/mechanize.html&#34;&gt;WWW::Mechanize&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Python: &lt;a href=&#34;http://www.crummy.com/software/BeautifulSoup/&#34;&gt;BeautifulSoup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Ruby: &lt;a href=&#34;http://wiki.github.com/why/hpricot&#34;&gt;HPricot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;PHP: &lt;a href=&#34;http://rainbowhatseo.fallenray.com/php5/php5-screen-scraping-with-dom-and-xpath/&#34;&gt;XPath&lt;/a&gt; (built-in)&lt;/li&gt;
&lt;li&gt;Javascript: &lt;a href=&#34;http://www.jquery.com/&#34;&gt;jQuery&lt;/a&gt; on &lt;a href=&#34;http://ejohn.org/projects/bringing-the-browser-to-the-server/&#34;&gt;env.js&lt;/a&gt; on &lt;a href=&#34;http://www.mozilla.org/rhino/&#34;&gt;Rhino&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But all of these are on the server side. That is, the program scrapes from &lt;strong&gt;your machine&lt;/strong&gt;. Can you write a web page where the &lt;strong&gt;viewer’s machine&lt;/strong&gt; does the scraping?&lt;/p&gt;
&lt;p&gt;Let’s take an example. I want to display &lt;a href=&#34;http://www.amazon.com/gp/bestsellers/books/&#34;&gt;Amazon&amp;rsquo;s bestsellers&lt;/a&gt; that cost less than $10. I could write a program that scrapes the site and get that information. But since the list updates hourly, I’ll have to run it every hour.&lt;/p&gt;
&lt;p&gt;That may not be so bad. But consider Twitter. I want to display the latest iPhone tweets from &lt;code&gt;http://search.twitter.com/search.atom?q=iPhone&lt;/code&gt;, but the results change so fast that your server can’t keep up.&lt;/p&gt;
&lt;p&gt;Nor do you want it to. Ideally, your scraper should just be Javascript on your web page. Any time someone visits, their machine does the scraping. The bandwidth is theirs, and you avoid the &lt;a href=&#34;http://www.codinghorror.com/blog/archives/000044.html&#34;&gt;popularity tax&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is quite easily done using &lt;a href=&#34;http://developer.yahoo.com/yql/&#34;&gt;Yahoo Query Language&lt;/a&gt;. YQL converts the web into a database. All web pages are in a table called &lt;code&gt;html&lt;/code&gt;, which has 2 fields: &lt;code&gt;url&lt;/code&gt; and &lt;code&gt;xpath&lt;/code&gt;. You can get IBM’s home page using:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;select * from html where url=&amp;quot;http://www.ibm.com&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Try it at &lt;a href=&#34;http://developer.yahoo.com/yql/console/&#34;&gt;Yahoo’s developer console&lt;/a&gt;. The whole page is loaded into the &lt;code&gt;query.results&lt;/code&gt; element. This can be retrieved using &lt;a href=&#34;http://en.wikipedia.org/wiki/JSONP#JSONP&#34;&gt;JSONP&lt;/a&gt;. Assuming you have jQuery, try the following on &lt;a href=&#34;http://getfirebug.com/&#34;&gt;Firebug&lt;/a&gt;. You should see the contents of IBM’s site on your page.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getJSON&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s2&#34;&gt;&amp;#34;http://query.yahooapis.com/v1/public/yql?callback=?&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;q&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;select * from html where url=\&amp;#34;http://www.ibm.com\&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;format&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;json&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;query&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;results&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That’s it! Now, it’s pretty easy to scrape, especially with &lt;a href=&#34;http://www.w3.org/TR/xpath&#34;&gt;XPath&lt;/a&gt;. To get the links on IBM’s page, just change the query to&lt;/p&gt;
&lt;p&gt;&lt;code&gt;select * from html where url=&amp;quot;http://www.ibm.com&amp;quot; and xpath=&amp;quot;//a&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Or to get all external links from IBM&amp;rsquo;s site:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;select * from html where url=&amp;quot;http://www.ibm.com&amp;quot; and xpath=&amp;quot;//a[not(contains(@href,&#39;ibm.com&#39;))][contains(@href,&#39;http&#39;)]&amp;quot;&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Now you can display this on your own site, using &lt;a href=&#34;http://www.jquery.com/&#34;&gt;jQuery&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This leads to interesting possibilities, such as &lt;a href=&#34;http://ajaxian.com/archives/map-reduce-in-the-browser&#34;&gt;Map-Reduce in the browser&lt;/a&gt;. Here’s one example. Each movie on the IMDb (e.g. &lt;a href=&#34;http://www.imdb.com/title/tt0468569/&#34;&gt;The Dark Knight&lt;/a&gt;) comes with a list of recommendations (like &lt;a href=&#34;http://www.imdb.com/title/tt0468569/recommendations&#34;&gt;this&lt;/a&gt;). I want to build a repository of recommendations based on the &lt;a href=&#34;http://www.imdb.com/chart/top&#34;&gt;IMDb Top 250&lt;/a&gt;. So here’s the algorithm. First, I’ll get the IMDb Top 250 using:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;select * from html where url=&amp;quot;http://www.imdb.com/chart/top&amp;quot; and xpath=&amp;quot;//tr//tr//tr//td[3]//a&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Then I’ll get a random movie’s recommendations like this:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;select * from html where url=&amp;quot;http://www.imdb.com/title/tt0468569/recommendations&amp;quot; and xpath=&amp;quot;//td/font//a[contains(@href,&#39;/title/&#39;)]&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Then I’ll send off the results to my aggregator.&lt;/p&gt;
&lt;p&gt;Check out the full code at &lt;a href=&#34;http://250.s-anand.net/build-reco.js&#34;&gt;http://250.s-anand.net/build-reco.js&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In fact, if you visited &lt;a href=&#34;http://250.s-anand.net/&#34;&gt;my IMDb Top 250 tracker&lt;/a&gt;, &lt;strong&gt;you already ran this code&lt;/strong&gt;. You didn’t know it, but you just shared a bit of your bandwidth and computation power with me. (Thank you.)&lt;/p&gt;
&lt;p&gt;And, if you think a little further, here another way of monetising content: by borrowing a bit of the user’s computation power to build complex tasks. There already are &lt;a href=&#34;http://www.pluraprocessing.com/&#34;&gt;startups&lt;/a&gt; built around this concept.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;comments&#34;&gt;Comments&lt;/h2&gt;
&lt;!-- wp-comments-start --&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&#34;http://aravindavk.in&#34;&gt;Aravinda&lt;/a&gt;&lt;/strong&gt; &lt;em&gt;4 Mar 2009 7:20 pm&lt;/em&gt;:
Great!&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sriram&lt;/strong&gt; &lt;em&gt;4 Mar 2009 10:51 pm&lt;/em&gt;:
Thank u anand. Nice Information.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rishi&lt;/strong&gt; &lt;em&gt;5 Mar 2009 8:19 am&lt;/em&gt;:
Awesome&amp;hellip; This is the future&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&#34;http://taeyoungchoon.wordpress.com/2009/03/26/grep-imdb-part-2/&#34;&gt;grep imdb part 2 ? « taeyoungchoon&lt;/a&gt;&lt;/strong&gt; &lt;em&gt;26 Mar 2009 1:16 pm&lt;/em&gt; &lt;em&gt;(pingback)&lt;/em&gt;:
[&amp;hellip;] &lt;a href=&#34;http://www.s-anand.net/blog/client-side-scraping/&#34;&gt;http://www.s-anand.net/blog/client-side-scraping/&lt;/a&gt; [&amp;hellip;]&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Khair&lt;/strong&gt; &lt;em&gt;26 May 2009 4:10 pm&lt;/em&gt;:
Many thanks for your efforts in simplifying the &amp;lsquo;scraping&amp;rsquo;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&#34;http://www.venkatsworld.com&#34;&gt;Venkat&lt;/a&gt;&lt;/strong&gt; &lt;em&gt;16 Nov 2010 1:40 pm&lt;/em&gt;:
Hey Anand,
Thanks. This post is really useful. I have managed to scrape Amazon wishlist using this technique (&lt;a href=&#34;http://www.venkatsworld.com/WIP/JSON&#34;&gt;http://www.venkatsworld.com/WIP/JSON&lt;/a&gt;_amazon.html) However YQL seems to be taking some time to respond. I am not sure if you experienced this.I did a similar exercise with Picasa JSON feed and a Jquery plug-in for image scrolling. It works well there too.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&#34;http://news.ycombinator.com/item?id=4066478&#34;&gt;Zheka&lt;/a&gt;&lt;/strong&gt; &lt;em&gt;17 Jun 2012 8:35 pm&lt;/em&gt;:
Interesting article! There is another powerful scraping technology available that offers Javascript, jQuery, CSS, and XPath instead of XPath-only. It&amp;rsquo;s called Bobik (&lt;a href=&#34;http://usebobik.com&#34;&gt;http://usebobik.com&lt;/a&gt;). The is a cool example of scraping restaurant menus using Bobik at &lt;a href=&#34;http://news.ycombinator.com/item?id=4066478&#34;&gt;http://news.ycombinator.com/item?id=4066478&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LAvesh&lt;/strong&gt; &lt;em&gt;12 Mar 2013 11:19 pm&lt;/em&gt;:
Hey Anand,
Great Article. Just one query though in the client side implementation its the client IP that will be hitting the website (in your case &lt;a href=&#34;https://www.ibm.com&#34;&gt;www.ibm.com&lt;/a&gt;)? or it will use yahoo SQL server IP to hit.&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- wp-comments-end --&gt;
</description>
    </item>
    <item>
      <title>Javascript error logging</title>
      <link>https://www.s-anand.net/blog/javascript-error-logging/</link>
      <pubDate>Tue, 25 Dec 2007 12:00:00 +0000</pubDate>
      <guid>https://www.s-anand.net/blog/javascript-error-logging/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;If something goes wrong with my site, I like to know of it. My top three problems are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&#34;https://www.s-anand.net/blog/monitoring-site-downtime/&#34;&gt;The site is down&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.s-anand.net/blog/handling-missing-pages/&#34;&gt;A page is missing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#start3&#34;&gt;Javascript isn&amp;rsquo;t working&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is the last of 3 articles on these topics.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;I am a bad programmer&lt;/strong&gt;
I am not a professional developer. In fact, I&amp;rsquo;m not a developer at all. I&amp;rsquo;m a management consultant. (Usually, it&amp;rsquo;s myself I&amp;rsquo;m trying to convince.)
Since no one pays me for what little code I write, no one shouts at me for getting it wrong. So I have a happy and sloppy coding style. I write what I feel like, and publish it. I don&amp;rsquo;t test it. Worse, sometimes, I don&amp;rsquo;t even run it once. I&amp;rsquo;ve sent little scripts off to people which wouldn&amp;rsquo;t even compile. I make changes to this site at midnight, upload it, and go off to sleep without checking if the change has crashed the site or not.
&lt;strong&gt;But no one tells me so&lt;/strong&gt;
At work, that&amp;rsquo;s usually OK. On the few occasions where I&amp;rsquo;ve written Perl scripts or VB Macros that don&amp;rsquo;t work, people call me back within a few hours, very worried that THEY&amp;rsquo;d done something wrong. (Sometimes, I don&amp;rsquo;t contradict them.) It can be quite a stressful experience but good thing you can &lt;a href=&#34;https://www.exhalewell.com/cbd-flower/&#34;&gt;&lt;strong&gt;learn more here&lt;/strong&gt;&lt;/a&gt; on how to cope up with it.
On my site, I don&amp;rsquo;t always get that kind of feedback. People just click the back button and go elsewhere.
Recently, I&amp;rsquo;ve been doing more Javascript work on my site than writing stuff. Usually, the code works for me. (I write it for myself in the first place.) But I end up optimising for Firefox rather than IE, and for the plugins I have, etc. When I try the same app a few months later on &lt;a href=&#34;https://www.s-anand.net/blog/making-a-media-pc/&#34;&gt;my media PC&lt;/a&gt;, it doesn&amp;rsquo;t work, and shockingly enough, no one&amp;rsquo;s bothered telling me about it all these months. They&amp;rsquo;d just click, nothing happens, they&amp;rsquo;d vanish.
&lt;strong&gt;But their browsers can tell me&lt;/strong&gt;
The good part about writing code in Javascript is that I can catch exceptions. Any Javascript error can be trapped. So since the end of last year, I&amp;rsquo;ve started wrapping almost every Javascript function I write in a &lt;code&gt;try {} catch() {}&lt;/code&gt; block. In the &lt;code&gt;catch&lt;/code&gt; block, I send a log message reporting the error.
The code looks something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;msg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;msg&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;=&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;src&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;log.pl?m=&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;encodeURIComponent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;msg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;abc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// ... function code
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;catch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;abc&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Any time there&amp;rsquo;s an error in &lt;code&gt;function abc&lt;/code&gt;, the &lt;code&gt;log&lt;/code&gt; function is called. It sends the function name (&lt;code&gt;&amp;quot;abc&amp;quot;&lt;/code&gt;) and the error details (the contents of the error event) to &lt;code&gt;log.pl&lt;/code&gt;, which stores the error, along with details like the URL, browser, time and IP address. This way, I know exactly where what error occurs.
This is a fantastic for a three reasons.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;It tells me &lt;strong&gt;when&lt;/strong&gt; I&amp;rsquo;ve goofed up&lt;/strong&gt;. This is instantaneous feedback. I don&amp;rsquo;t have to wait for a human. If you run my program on your machine, and it fails, &lt;strong&gt;I get to know immediately.&lt;/strong&gt; (Well, as soon as I read the error log, at least.)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It tells me &lt;strong&gt;where&lt;/strong&gt; I&amp;rsquo;ve goofed up&lt;/strong&gt;. The URL and the function name clearly indicate the point of failure.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It tells me &lt;strong&gt;why&lt;/strong&gt; I&amp;rsquo;ve goofed up&lt;/strong&gt;. Almost. Using the browser name and the error message, I can invariably pinpoint the reason for the error. Then it&amp;rsquo;s just a matter of taking the time to fix it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&amp;rsquo;d think this sort of error reporting should be the norm for any software. At least for a web app, given how easy it is to implement.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;comments&#34;&gt;Comments&lt;/h2&gt;
&lt;!-- wp-comments-start --&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CodeKitten&lt;/strong&gt; &lt;em&gt;25 Dec 2007 12:00 pm&lt;/em&gt;:
You should bind an onerror-triggered method to call your log method aswell :)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Danko Stojanovic&lt;/strong&gt; &lt;em&gt;10 May 2010 8:12 am&lt;/em&gt;:
I feel your pain. I also moved away from programming to management. I have to live with customers saying &amp;ldquo;It doesn&amp;rsquo;t work&amp;rdquo; and programmers &amp;ldquo;It works just fine when I test it&amp;rdquo;.
I so wish to know what is going on my client&amp;rsquo;s computer.
Thanks for this article. It helped me decide to pursue this further&lt;/li&gt;
&lt;li&gt;
&lt;h2 id=&#34;allan-ebdrup-13-nov-2011-723-amthe-onerror-event-is-a-great-way-to-start-logging-javascript-errors-more-people-should-use-it-javascript-errors-can-be-just-as-bad-as-a-webserver-that-is-down-you-really-need-to-log-those-errors-so-you-can-do-something-about-themonerror-is-quirky-it-does-not-fire-on-ipad-iphone-safari-and-some-other-browseres-to-catch-those-errors-you-have-to-insert-try-catches-arount-your-event-handlers-xhr-requests-and-some-other-places-this-will-also-let-you-get-more-detail-about-the-errror-call-stack-in-some-browsersbut-onerror-is-still-much-better-than-nothing&#34;&gt;&lt;strong&gt;&lt;a href=&#34;http://muscula.com&#34;&gt;Allan Ebdrup&lt;/a&gt;&lt;/strong&gt; &lt;em&gt;13 Nov 2011 7:23 am&lt;/em&gt;:The onerror event is a great way to start logging JavaScript errors, more people should use it. JavaScript errors can be just as bad as a webserver that is down, you really need to log those errors so you can do something about them.onerror, is quirky. It does not fire on iPad, iPhone, Safari and some other browseres. To catch those errors you have to insert try-catch&amp;rsquo;es arount your event handlers, xhr-requests and some other places. This will also let you get more detail about the errror (call stack in some browsers).But onerror is still much better than nothing.&lt;/h2&gt;
We&amp;rsquo;ve solved a lot of these issues, with a solution that you install just like Google Analytics. Currently we are in a testing phase, private beta (free). To get an invite you can got to &lt;a href=&#34;http://muscula.com&#34;&gt;http://muscula.com&lt;/a&gt; and sign up.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kiawaki&lt;/strong&gt; &lt;em&gt;8 Jul 2011 5:45 am&lt;/em&gt;:
A question from somebody who is even less of a pro web developer: how exactly do you use this code? Do you put it before or after the script or both? Thanks!&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&#34;http://jserrlog.appspot.com&#34;&gt;Offbeatmammal&lt;/a&gt;&lt;/strong&gt; &lt;em&gt;23 Sep 2011 12:48 am&lt;/em&gt;:
if you want a no-brain, minimal effort then have a look at jsErrLog &amp;hellip; a simple utility that does this and logs it to a remote AppEngine service (all for free, in an open source project) - check out &lt;a href=&#34;http://jsErrLog.appspot.com&#34;&gt;http://jsErrLog.appspot.com&lt;/a&gt; which links to the GitHub repository as well&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- wp-comments-end --&gt;
</description>
    </item>
    <item>
      <title>Mozilla</title>
      <link>https://www.s-anand.net/blog/mozilla/</link>
      <pubDate>Tue, 03 Sep 2002 12:00:00 +0000</pubDate>
      <guid>https://www.s-anand.net/blog/mozilla/</guid>
      <description>&lt;p&gt;I&amp;rsquo;m trying &lt;a href=&#34;http://www.mozilla.org&#34;&gt;Mozilla&lt;/a&gt;. It has &lt;a href=&#34;http://www.oreillynet.com/pub/a/javascript/synd/2002/08/30/mozillasoapapi.html&#34;&gt;native SOAP support&lt;/a&gt;. So I can make a web page that has dynamic Google searches (and Amazon booklists, etc) without a web-server.&lt;/p&gt;
</description>
    </item>
  </channel>
</rss>
